方格取数
原题链接
题目描述
设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。
如下图所示:
某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A 点到 B 点共走了两次 ,试找出两条 这样的路径,使得取得的数字和为最大。
输入格式
第一行为一个整数N,表示 N×N 的方格图。
接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
行和列编号从 1开始。
一行"0 0 0"表示结束。
输出格式
输出一个整数,表示两条路径上取得的最大的和。
数据范围
N≤10
输入样例:
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
题目分析
与 摘花生 类似,不同点在于该题方格取数 要找出两条路径,使其和最大 。
回忆 摘花生 动态规划数字三角形模型思考。
摘花生(只走一次):
f [ i , j ] f[i,j] f[i,j] 表示左右从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 的路径的最大值
f [ i , j ] = m a x ( f [ i − 1 , j ] + w [ i ] [ j ] , f [ i , j − 1 ] + w [ i , j ] ) f[i,j]=max(f[i-1,j]+w[i][j],f[i,j-1]+w[i,j]) f[i,j]=max(f[i−1,j]+w[i][j],f[i,j−1]+w[i,j])
方格取数(走两次):
利用摘花生的思想,同时维护两条路径 ,用 f [ i 1 , j 1 , i 2 , j 2 ] f[i1,j1,i2,j2] f[i1,j1,i2,j2] 表示所有从 ( 1 , 1 ) , ( 1 , 1 ) (1,1), (1,1) (1,1),(1,1) 分别走到 ( i 1 , j 1 ) , ( i 2 , j 2 ) (i1,j1), (i2,j2) (i1,j1),(i2,j2) 的路径的最大值。
同时,每走一格,要么是 i + 1 i+1 i+1,要么是 j + 1 j+1 j+1,并且由于是同时出发的两条路径 ,则一定满足** i 1 + j 1 = i 2 + j 2 i1+j1=i2+j2 i1+j1=i2+j2** 。据此,我们可以化简状态 f [ i 1 , j 1 , i 2 , j 2 ] f[i1,j1,i2,j2] f[i1,j1,i2,j2],令 k = i 1 + j 1 = i 2 + j 2 k=i1+j1=i2+j2 k=i1+j1=i2+j2 ,表示两条路径走到的格子的横纵坐标之和 。
f [ k , i 1 , i 2 ] f[k,i1,i2] f[k,i1,i2] 表示所有从 ( 1 , 1 ) , ( 1 , 1 ) (1,1), (1,1) (1,1),(1,1) 分别走到 ( i 1 , k − i 1 ) , ( i 2 , k − i 2 ) (i1,k-i1), (i2,k-i2) (i1,k−i1),(i2,k−i2) 的路径的最大值。
但需要注意的是,当两条路径走到同一个格子时,一个方格中的数字只能被取一次,则如何处理"同一个格子不能被重复选择 "?
我们知道,只有在 i 1 + j 1 = i 2 + j 2 i1+j1=i2+j2 i1+j1=i2+j2 时,两条路径的格子才可能重合。即为在同一个 k k k 下,同时满足 i 1 = i 2 i1=i2 i1=i2 时,两条路径的格子重合。此时,该方格内的数字只被加一次。
状态计算
用 t t t 表示下一步需要取的数字之和,
若两条路径不重合,则 t = w [ i 1 ] [ j 1 ] + w [ i 2 ] [ j 2 ] t=w[i1][j1]+w[i2][j2] t=w[i1][j1]+w[i2][j2],
若两条路径重合,则 t = w [ i 1 ] [ j 1 ] t=w[i1][j1] t=w[i1][j1](只用加一次)。
状态的转移计算示意图如下:
路径从左至右:则由 i − 1 i-1 i−1至 i i i,由 k − 1 k-1 k−1至 k k k。
路径从上至下:则 i i i 不变,由 k − 1 k-1 k−1至 k k k。
第一条路径:从左至右;第二条路径:从左至右:
f [ k ] [ i 1 ] [ i 2 ] = m a x ( f [ k ] [ i 1 ] [ i 2 ] , f [ k − 1 ] [ i 1 − 1 ] [ i 2 − 1 ] + t ) f[k][i1][i2]=max(f[k][i1][i2], f[k-1][i1-1][i2-1]+t) f[k][i1][i2]=max(f[k][i1][i2],f[k−1][i1−1][i2−1]+t)
第一条路径:从左至右;第二条路径:从上至下:
f [ k ] [ i 1 ] [ i 2 ] = m a x ( f [ k ] [ i 1 ] [ i 2 ] , f [ k − 1 ] [ i 1 − 1 ] [ i 2 ] + t ) f[k][i1][i2]=max(f[k][i1][i2], f[k-1][i1-1][i2]+t) f[k][i1][i2]=max(f[k][i1][i2],f[k−1][i1−1][i2]+t)
第一条路径:从上至下;第二条路径:从上至下:
f [ k ] [ i 1 ] [ i 2 ] = m a x ( f [ k ] [ i 1 ] [ i 2 ] , f [ k − 1 ] [ i 1 ] [ i 2 ] + t ) f[k][i1][i2]=max(f[k][i1][i2], f[k-1][i1][i2]+t) f[k][i1][i2]=max(f[k][i1][i2],f[k−1][i1][i2]+t)
第一条路径:从上至下;第二条路径:从左至右:
f [ k ] [ i 1 ] [ i 2 ] = m a x ( f [ k ] [ i 1 ] [ i 2 ] , f [ k − 1 ] [ i 1 ] [ i 2 − 1 ] + t ) f[k][i1][i2]=max(f[k][i1][i2], f[k-1][i1][i2-1]+t) f[k][i1][i2]=max(f[k][i1][i2],f[k−1][i1][i2−1]+t)
完整代码
cpp
#include <iostream>
#include <algorithm>
using namespace std;
const int N=15;
int n;
int w[N][N]; //权重
int f[N*2][N][N]; //状态计算
int main(){
scanf("%d",&n);
int a,b,c;
while(cin>>a>>b>>c,a||b||c) w[a][b]=c;
for(int k=2;k<=n+n;k++){
for(int i1=1;i1<=n;i1++){
for(int i2=1;i2<=n;i2++){
int j1=k-i1,j2=k-i2;
if(j1>=1&&j1<=n&&j2>=1&&j2<=n){ //j符合条件
int t=w[i1][j1]; //第一条路径
if(i1!=i2) t+=w[i2][j2]; //如果第二条路径与第一条路径重合,则权重w[i][j]不能重复增加
int &x=f[k][i1][i2]; //使用&简化代码
x=max(x,f[k-1][i1-1][i2-1]+t); //第一条路径:来自上方;第二条路径:来自上方
x=max(x,f[k-1][i1-1][i2]+t); //第一条路径:来自上方;第二条路径:来自左方
x=max(x,f[k-1][i1][i2]+t); //第一条路径:来自左方;第二条路径:来自左方
x=max(x,f[k-1][i1][i2-1]+t); //第一条路径:来自左方;第二条路径:来自上方
}
}
}
}
printf("%d\n",f[n+n][n][n]);
return 0;
}