详情看代码。
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
const int N=1010;
long long a[N][N],f[N][N][3];
/*
状态定义:
f(i,j,0):从上一个格子走到当前格子能取到的最大值
f(i,j,1):从下一个格子走到当前格子能取到的最大值
f(i,j,2):从左边格子走到当前格子能取到的最大值
状态初始化:
将f数组初始化为负无穷。
起点当然要特殊处理:
f(1,1,0/1/2)=a(1,1)
可知,第一列的数只能从上往下转移而来
因此可以用递推来初始化
f(i,1,0)=f(i-1,1,0)+a(i,1)
转移顺序:
因为涉及到上与下两个方向,而左右只有向右走一个选择。
因此,我们可以考虑外层枚举列,内层枚举行。
这样方便一点。
状态转移:
f(i,j,0)=max(f(i-1,j,0),f(i-1,j,2)+a(i,j)
为什么没有f(i-1,j,1)呢?
题目中明确提到:每一个点不可以重复遍历
(i-1,j)下面的点就是(i,j),那不就违反规定了吗?
f(i,j,1)类似:
f(i,j,1)=max(f(i-1,j,1),f(i-1,j,2)+a(i,j)
因为只能向右走,因此如果水平方向移动的话不必考虑算重:
f(i,j,2)=max(f(i,j-1,0),max(f(i,j-1,1),f(i,j-1,2)))+a(i,j)
答案:
max(f(n,m,0),max(f(n,m,1),f(n,m,2)))
当然,你可以把f(n,m,1)去掉,不影响结果
(反正(n,m)不可能从下面转移过来)
注意事项:
1.边界。不要越界(如果数组从1开始可以忽略)
2.long long。我就被坑一次
*/
int main(){
memset(f,-0x3f,sizeof f);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
f[1][1][0]=f[1][1][1]=f[1][1][2]=a[1][1];
for(int i=2;i<=n;i++){
f[i][1][0]=f[i-1][1][0]+a[i][1];
}
for(int j=2;j<=m;j++){
for(int i=1;i<=n;i++){
f[i][j][2]=max(f[i][j-1][0],max(f[i][j-1][1],f[i][j-1][2]))+a[i][j];
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][2])+a[i][j];
}
for(int i=n-1;i;i--){
f[i][j][1]=max(f[i+1][j][1],f[i+1][j][2])+a[i][j];
}
}
cout<<max(f[n][m][0],f[n][m][2]);
}
/*
总结:
普通方格取数都是要么下右、下左......
三个方向还真是头一回见。
但是只要你没搞错题意再加上一点思考,应该也是能做的。
好题!
*/