题目描述

链接:https://ac.nowcoder.com/acm/problem/16759
来源:牛客网

设有N*N的方格图(N ≤ 10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):
图片说明
某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

题目思路

因为是走两次,所以根据动态规划的思想,可以认为是两个人同时走。因此可以用四维数组dp[i][j][k][p]来表示两个人到到当前位置时的取数之和。其中,(i,j) 表示第一个人的位置;(k,p)表示第二个人的位置。
由于每个人的走到当前位置的可能性有两种,所以组合起来就有四种构成dp[i][j][k][p]的方式,状态转移方程如下:
dp[i][j][k][p] = max(max(dp[i-1][j][k-1][p], dp[i][j-1][k-1][p]), max(dp[i-1][j][k][p-1], dp[i][j-1][k][p-1])) + a[i][j] + a[k][p];
有可能这两个人走到了同一个位置,此时该位置上的数只用被记录一遍:
dp[i][j][k][p] = max(max(dp[i-1][j][k-1][p], dp[i][j-1][k-1][p]), max(dp[i-1][j][k][p-1], dp[i][j-1][k][p-1])) + a[i][j];
由于是利用四重循环得到的dp数组,所以每一种路径重复的情况都会在循环时被计算过,因此不必再进行置零处理。

完整代码

#include<bits/stdc++.h>
using namespace std;
int a[15][15] = {0};
int dp[15][15][15][15];
int main()
{
    int n;
    scanf("%d",&n);
    int i = n; 
    while(1)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(x+y+z!=0)
            a[x][y] = z;
        else break;
    }    
    dp[1][1][1][1] = a[1][1];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int k=1;k<=n;k++)
            {
                for(int p=1;p<=n;p++)
                {
                    if(i==k && j==p)
                        dp[i][j][k][p] = max(max(dp[i-1][j][k-1][p], dp[i][j-1][k-1][p]), max(dp[i-1][j][k][p-1], dp[i][j-1][k][p-1])) + a[i][j];

                    else 
                        dp[i][j][k][p] = max(max(dp[i-1][j][k-1][p], dp[i][j-1][k-1][p]), max(dp[i-1][j][k][p-1], dp[i][j-1][k][p-1])) + a[i][j] + a[k][p];

                }
            }
        }
    }
    printf("%d\n",dp[n][n][n][n]);
    return 0;
}

总结

刚开始的思路是利用贪心,先求出一条和最大的路径,更新a地图,再重新遍历取得另一条最大路径。虽然样例过了,但是还是不能通过全部测试用例。这种情况应该引以为戒,再出现这种多次求极值的问题时,应该综合起来考虑,通过一次动归完成。
不过从错误的想法中我又复习了怎样通过dp方程还原路径,所以代码附下:

#include<bits/stdc++.h>
using namespace std;
long long dp[15][15];
int main()
{
    int n;
    int grid[15][15] = {0};
    long long ans = 0;
    scanf("%d",&n); 
    while(1)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(a+b+c)
        {
            grid[a][b] = c;
        }
        else break;
    }

    dp[1][1] = grid[1][1];
    for(int i=2;i<=n;i++)
    {
        dp[i][1] = dp[i-1][1] + grid[i][1];
        dp[1][i] = dp[1][i-1] + grid[1][i];
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=2;j<=n;j++)
        {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j];
        }
    }

    int ok=0;
    int x = n, y = n;
    while(!ok)
    {
        grid[x][y] = 0;
        //printf("(%d, %d)\n",x,y);
        if(x>1 && y>1 && dp[x-1][y] >= dp[x][y-1] )
        {
            x = x-1;
            y = y;
        }
        else if(x>1 && y>1 && dp[x-1][y] < dp[x][y-1])
        {
            x = x;
            y = y-1;
        }
        else if(x == 1)
        {
            for(int i=y;i>0;i--)
            {
                grid[1][i] = 0;
            }    
            ok = 1;
            break;
        }
        else if(y == 1)
        {
            for(int i=x;i>0;i--)
            {
                grid[i][1] = 0;
            }
            ok = 1;
            break;
        }
        if(x == 1&&y == 1)
        {
            ok = 1;
            grid[1][1] = 0;
            break;
        }
    }
    ans = dp[n][n];
    memset(dp, 0, sizeof(dp));
    for(int i=2;i<=n;i++)
    {
        dp[i][1] = dp[i-1][1] + grid[i][1];
        dp[1][i] = dp[1][i-1] + grid[1][i];
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=2;j<=n;j++)
        {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j];
        }
    }
    ans = ans + dp[n][n];
    printf("%lld\n", ans);
    return 0;
}