设有 N \times NN×N 的方格图 (N \le 9)(N≤9) ,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 00 。如下图所示(见样例):
A
0 0 0 0 0 0 0 0
0 0 13 0 0 6 0 0
0 0 0 0 7 0 0 0
0 0 0 14 0 0 0 0
0 21 0 0 0 4 0 0
0 0 15 0 0 0 0 0
0 14 0 0 0 0 0 0
0 0 0 0 0 0 0 0
B
某人从图的左上角的 AA 点出发,可以向下行走,也可以向右走,直到到达右下角的 BB 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 00 )。
此人从 AA 点到 BB 点共走两次,试找出 22 条这样的路径,使得取得的数之和为最大。
输入的第一行为一个整数 NN (表示 N \times NN×N 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 00 表示输入结束。
输出格式:只需输出一个整数,表示 22 条路径上取得的最大的和。
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
NOIP 2000 提高组第四题
这道题和传纸条的异同:
异: 这道题允许交叠在一起,而传纸条不允许交叠在一起
方法一:四维动归
#include
#include
#define MAXN 55
using namespace std;
int a[MAXN][MAXN];
int dp[MAXN][MAXN][MAXN][MAXN];
//dp[i][j][k][l]表示从(0, 0)位置由两条不交叉的线路走到(i, j)(k, l)位置时的最大好感度和
//两条不相交的线路,虽然一从左上,一个从右下,但因为算的是最大值,于是可以都看成是从左上出发,到右下,规定一条为左,一条为右,这样避免了重复
//这样前进的方向只能向下,或向右
//状态转移方程:
//max_four(f[i][j-1][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k][l-1],f[i-1][j][k-1][l])+a[i][j]+a[k][l]; 前一个状态有四个
int max_four(int a,int b,int c,int d)
{
if(b>a)
a=b;
if(c>a)
a=c;
if(d>a)
a=d;
return a;
}
int main()
{
int n;
int i,j,x,y,k,l;
cin>>n;
while(scanf("%d%d%d",&x,&y,&k),x)
{
a[x][y]=k;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
for(k=1;k<=n;k++)
for(l=1;l<=n;l++) //与传纸条的区别:这里不在分左右了,都可以
if(i==k&&j==l) //交叠处就加一次,因为加了一次,他就变为0了,所以只加一次
dp[i][j][k][l]=max_four(dp[i][j-1][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k][l-1],dp[i-1][j][k-1][l])+a[i][j];
else
dp[i][j][k][l]=max_four(dp[i][j-1][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k][l-1],dp[i-1][j][k-1][l])+a[i][j]+a[k][l];
cout << dp[n][n][n][n] << endl; //终点要考虑上
return 0;
}
方法2:斜线法
#include
#include
#include
using namespace std;
#define MAXN 55
int a[MAXN][MAXN];
int dp[2*MAXN][MAXN][MAXN];//dp[k][i][j]表示的是第k条斜线上,以第i列,第j列的两个点为进出口时,得到路径的最大值。
//因为是同步前进,所以可以用斜线来表示,即斜线可以表示同步前进的两条路线。
//四种前一状态:dp[k-1][i][j]; //1下,2下
// dp[k-1][i][j-1]; //1下,2右
// dp[k-1][i-1][j]; //1右,2下
// dp[k-1][i-1][j-1]; //1右,2右
int main()
{
int n;
int i,j,x,y,k;
cin>>n;
while(scanf("%d%d%d",&x,&y,&k),x)
{
a[x][y]=k;
}
memset(dp,-1,sizeof(dp)); //赋初值为-1 (原因在后面)
dp[2][1][1]=a[1][1]; //最初的点,由地图上的点决定
for(k=3;k<=2*n-1;k++) //k的范围(3~m+n-1) ,即共有m+n-3条斜线
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
int s=dp[k][i][j]; //用斜线法会有越界的情况。
s=max(max(dp[k-1][i][j],dp[k-1][i-1][j]),max(dp[k-1][i][j-1],dp[k-1][i-1][j-1])); //这只是简单的递推关系,所以不需要和之前进行比较
if(s==-1)continue; //当s为-1时,说明四种情况都不能到该点,故不存在。
if(i==j)
dp[k][i][j]=s+a[k-i][i];
else
dp[k][i][j]=s+a[k-i][i]+a[k-j][j];
}
printf("%d\n",dp[2*n-1][n-1][n]+a[n][n]); //这里又是四维动归与斜线法的区别所在了,最后的终点是无法用斜线算到的,所以加上一次最后点的值
return 0;
}