P1004 方格取数(四维动归与斜线法)

题目描述

设有 N \times NN×N 的方格图 (N \le 9)(N9) ,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 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 条路径上取得的最大的和。

输入输出样例

输入样例#1:  复制
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
输出样例#1:  复制
67

说明

NOIP 2000 提高组第四题


这道题和传纸条的异同:

异: 这道题允许交叠在一起,而传纸条不允许交叠在一起

方法一:四维动归

#include<stdio.h>
#include <iostream>
#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 <iostream>
#include <cstdio>
#include <cstring>
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;
 }

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/81053189