一、问题描述
二、思路分析
这道题非常类似我们数字三角形那道题,大家如果这道题不会的话,可以先去看作者之前写的数字三角形问题的解法,然后再回来看这道题,或许就能有思路了。
传送门:
DP母题——数字三角形
1、状态表示
f ( i , j ) f(i,j) f(i,j)表示的是从起点 ( 0 , 0 ) (0,0) (0,0)走到 ( i , j ) (i,j) (i,j)的过程中,可能摘走的花生的数目中的最大值。
2、状态转移
状态转移方程的书写,要么看当前状态能够推导出哪些状态,要么看当前状态能够从哪些状态推导而来。这两个角度也是我们在做DP问题的时候经常使用的方法。
当前的点要么从正上方的点移动而来,要么由左边的点移动而来。
我们在这两种可能当中,取一个花生数目最多的状态, 再加上当前状态的花生数目就是我们的当前的状态。
f ( i , j ) = { m a x ( f ( i − 1 , j ) , + f ( i , j − 1 ) ) + w [ i ] [ j ] i , j ≥ 1 f ( i − 1 , j ) + w [ i ] [ j ] i ≥ 1 , j < 1 f ( i , j − 1 ) + w [ i ] [ j ] j ≥ 1 , i < 1 f(i,j)= \begin{cases} max \big( f(i-1,j),+f(i,j-1)\big)+w[i][j] & i,j\geq 1\\ f(i-1,j)+w[i][j] &i\geq 1,j<1\\ f(i,j-1)+w[i][j] &j\geq 1,i<1 \end{cases} f(i,j)=⎩ ⎨ ⎧max(f(i−1,j),+f(i,j−1))+w[i][j]f(i−1,j)+w[i][j]f(i,j−1)+w[i][j]i,j≥1i≥1,j<1j≥1,i<1
3、循环设计
循环设计的设计要符合拓扑排序,说白了就是要在计算当前状态的时候,已经计算出了等式右边子问题的状态。
这道题很明显,i 和 j 谁做外循环都可以,所以大家二选一即可。
4、初末状态
初始化的是最小子问题,这道题的话,就是我们站在起点的时候,所携带的花生数目。很明显,站在起点(0,0)的初始状态,就是(0,0)点的花生数目。
末状态是为了能够表示出答案并输出。
这道题就是
f
[
r
−
1
]
[
c
−
1
]
f[r-1][c-1]
f[r−1][c−1],因为下标从0开始,所以右下角的坐标-1。
三、代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int f[N][N],g[N][N];
int t,r,c;
int main()
{
cin>>t;
while(t--)
{
scanf("%d%d",&r,&c);
for(int i=0;i<r;i++)
for(int j=0;j<c;j++)
scanf("%d",&g[i][j]);
f[0][0]=g[0][0];
for(int i=0;i<r;i++)
{
for(int j=0;j<c;j++)
{
if(j<1&&i>=1)
f[i][j]=f[i-1][j]+g[i][j];
else if(i<1&&j>=1)
f[i][j]=f[i][j-1]+g[i][j];
else if(i>=1&&j>=1)
f[i][j]=max(f[i][j-1]+g[i][j],f[i-1][j]+g[i][j]);
}
}
cout<<f[r-1][c-1]<<endl;
}
return 0;
}