一道dp题。难度不大,但是个典型的dp考法,值得一说。
看一眼题,容易写出dp方程ans[i][j]=max{ans[i-1][j],ans[i][j-1],ans[i+1][j]}+val[i][j]。但发现难以确定dp递推的顺序。但思考一会后,又发现dp方程可拆分成两种情况,因为不能走重复的格子,所以对于当前的格子(i,j),要么从上方、左边走过来,且同一列的路径格子的进入方式也为上方/左边;要么从下方、左边走过来,且同一列的路径格子的进入方式也为下方/左边。即dp状态转移可拆分成两种情况,且在其中一种情况中,转移方程都一样。拆分后容易找到明显的转移顺序,最后再合并一下就好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 4 using namespace std; 5 6 const int N=1e3+5; 7 const long long FINF=-20000000000ll; 8 9 int n,m; 10 11 long long up[N][N],down[N][N],ans[N][N],mp[N][N]; 12 13 inline int read()//Z 14 { 15 int x=0; 16 char ch=getchar(); 17 bool f=0; 18 while(!isdigit(ch)) 19 f|=ch=='-',ch=getchar(); 20 while(isdigit(ch)) 21 x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 22 return f?-x:x; 23 } 24 25 int main() 26 { 27 n=read(),m=read(); 28 for(int i=1;i<=n;++i) 29 for(int j=1;j<=m;++j) 30 mp[i][j]=read(); 31 for(int i=1;i<=n;++i) 32 ans[i][1]=ans[i-1][1]+mp[i][1]; 33 for(int j=2;j<=m;++j) 34 { 35 up[0][j]=down[n+1][j]=FINF; 36 for(int i=1;i<=n;++i) 37 up[i][j]=max(up[i-1][j],ans[i][j-1])+mp[i][j]; 38 for(int i=n;i>=1;--i) 39 down[i][j]=max(down[i+1][j],ans[i][j-1])+mp[i][j]; 40 for(int i=1;i<=n;++i) 41 ans[i][j]=max(up[i][j],down[i][j]); 42 } 43 printf("%lld",ans[n][m]); 44 return 0; 45 }