保持沉默,是你的自由。
题目链接
题目描述
设有 N×N 的方格图 (N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0。如下图所示(见样例):
某人从图的左上角的 A点出发,可以向下行走,也可以向右走,直到到达右下角的 B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。
此人从 A 点到 B点共走两次,试找出 2 条这样的路径,使得取得的数之和为最大。
输入格式
输入的第一行为一个整数 N(表示 N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 0表示输入结束。
输出格式
只需输出一个整数,表示 2条路径上取得的最大的和。
输入输出样例
输入
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
输出
67
一开始的思路:
每走到一个位置,有两种选择,向下或向右,那怎么选择向下或向右呢?我觉得应该比较这个位置的右边和还有左边和,哪个大就往哪边走,然后把走过的位置变成0,再用这种算法走一遍
#includeusing namespace std; int a[10][10]; int main(){ int n,i,j; cin>>n; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ a[i][j]=0; } } int x,y,z; while(1){ cin>>x>>y>>z; a[x][y]=z; if(x==0) break; } int cnt=0; int rx=1,ry=1; if(a[1][1]!=0){ cnt+=a[1][1]; a[1][1]=0; } while(rx<=n&&ry<=n){ int sumx=0,sumy=0; for(i=ry+1;i<=n;i++){ sumx+=a[rx][i]; } for(i=rx+1;i<=n;i++){ sumy+=a[i][ry]; } if(sumx>=sumy) ry=ry+1; else if(sumx cnt+=a[rx][ry]; a[rx][ry]=0; } } rx=1,ry=1; while(rx<=n&&ry<=n){ int sumx=0,sumy=0; for(i=ry+1;i<=n;i++){ sumx+=a[rx][i]; } for(i=rx+1;i<=n;i++){ sumy+=a[i][ry]; } if(sumx>=sumy) ry=ry+1; else if(sumx cnt+=a[rx][ry]; a[rx][ry]=0; } } cout< 然后只得了40分
发现这种算法是不对的,比如这个样例就过不了:
0 10 7 4 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 3 0 0 0 0 0
0 0 6 0 0 0 0 0 0
5 0 7 0 3 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
最大33
走完一遍走第二遍的时候
0 0 0 4 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 3 0 0 0 0 0
0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
按我的算法最大是5,但其实可以走4+3
于是看一下题目标签,发现是动态规划
然后我又开始了dp
设两个dp函数,先dp一遍,记录路径(自己写出记录路径还觉得自己是个天才,我真傻比),顺着路径逆推把走过的置0,再走一遍#includeusing namespace std; int f1[10][10],f2[10][10],p[10][10]; int a[10][10]; int main(){ int n,i,j; cin>>n; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ a[i][j]=0; } } int x,y,z; while(1){ cin>>x>>y>>z; a[x][y]=z; if(x==0) break; } for(i=1;i<=n;i++){ f1[1][i]=a[1][i]+f1[1][i-1]; f1[i][1]=a[i][1]+f1[i-1][1]; } for(i=2;i<=n;i++){ for(j=2;j<=n;j++){ if(f1[i-1][j]>=f1[i][j-1]){ f1[i][j]=f1[i-1][j]+a[i][j]; p[i][j]=1; } else{ f1[i][j]=f1[i][j-1]+a[i][j]; p[i][j]=0; } } } if(a[n][n]!=0) a[n][n]=0; int rx=n,ry=n; while(rx>=1&&ry>=1){ if(p[rx][ry]==1){ a[rx-1][ry]=0; rx=rx-1; } else if(p[rx][ry]==0){ a[rx][ry-1]=0; ry=ry-1; } if(rx==1&&ry==1) break; } for(i=1;i<=n;i++){ f2[1][i]=a[1][i]+f2[1][i-1]; f2[i][1]=a[i][1]+f2[i-1][1]; } for(i=2;i<=n;i++){ for(j=2;j<=n;j++){ if(f2[i-1][j]>=f2[i][j-1]){ f2[i][j]=f2[i-1][j]+a[i][j]; p[i][j]=1; } else{ f2[i][j]=f2[i][j-1]+a[i][j]; p[i][j]=0; } } } cout< 这个想法,非常的好,然后得了80分。。
这个样例就过不了
0 0 2 3 0 0 0
0 0 3 0 0 0 0
0 0 3 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 4 0 0
0 0 0 0 4 0 0
0 0 2 0 4 0 0
最大20
dp走完一遍
0 0 0 3 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 2 0 0 0 0
最大3
加起来23,正确答案25
哦,我想了下,如果第一遍不取最大,第二遍再走,其实是能取到所有数的,所以还是不对
最后我就去看题解了,发现世界上居然有四维dp这种东西,震惊
所以这道题的正解应该是四维dp+特判,哎。真的无语了。#includeusing namespace std; int f[10][10][10][10]; int a[10][10]; int main(){ int n,i,j,k,l; cin>>n; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ a[i][j]=0; } } int x,y,z; while(1){ cin>>x>>y>>z; a[x][y]=z; if(x==0) break; } for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ for(k=1;k<=n;k++){ for(l=1;l<=n;l++){ f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])))+a[i][j]+a[k][l]; if(i==k&&j==l) f[i][j][k][l]-=a[i][j];//如果某个位置两条路径重合了,只能加一个的数 } } } } cout< 又要感慨吾生也有涯知也无涯了,再多做点题吧


![洛谷P1004 [NOIP2000 提高组] 方格取数 四维动态规划 洛谷P1004 [NOIP2000 提高组] 方格取数 四维动态规划](http://www.mshxw.com/aiimages/31/884096.png)
