保持沉默,是你的自由。
题目链接
题目描述
设有 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,再用这种算法走一遍
#include<bits/stdc++.h>
using 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<sumy) rx=rx+1;
if(a[rx][ry]!=0) {
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<sumy) rx=rx+1;
if(a[rx][ry]!=0) {
cnt+=a[rx][ry];
a[rx][ry]=0;
}
}
cout<<cnt<<endl;
return 0;
}
然后只得了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,再走一遍
#include<bits/stdc++.h>
using 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<<f1[n][n]+f2[n][n]<<endl;
return 0;
}
这个想法,非常的好,然后得了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+特判,哎。真的无语了。
#include<bits/stdc++.h>
using 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<<f[n][n][n][n]<<endl;
return 0;
}
又要感慨吾生也有涯知也无涯了,再多做点题吧