题解 P1006 【传纸条】

· · 题解

感觉大家都很喜欢写四维DP。

我来发个三维DP(虽然也没多少区别QAQ..)。

这题和前面1004方格取数一毛一样。

首先,要找来回两条路径,这样考虑太麻烦,把它转化为两个人从1,1这点一起走,一直走到n,m这点所经过的路径。

定义f[p][i][j]表示当前走了p步,第一个人走到第i行,第二个人走到第j行的最大价值。

显然两个人的坐标都可以计算出来,第一个人是(i,p-i+1),第二个人是(j,p-j+1)。

转移就考虑两个人的上一步是怎样走的。

f[p][i][j] = max(max(f[p - 1][i][j], f[p - 1][i - 1][j]), max(f[p - 1][i][j - 1], f[p - 1][i - 1][j - 1])) + a[i][p - i + 1] + a[j][p - j + 1]。

由于两条路径经过同一个点的价值只能算一次,所以如果当前i==j(相当于两个人的位置重合了),我们只能算一遍该点的价值。

所以整个转移就是这样了:

f[p][i][j] = max(max(f[p - 1][i][j], f[p - 1][i - 1][j]), max(f[p - 1][i][j - 1], f[p - 1][i - 1][j - 1]));

f[p][i][j] += i == j ? a[i][p - i + 1] : a[i][p - i + 1] + a[j][p - j + 1];

是不是很简单。。

几个注意事项:

1.数组别开小,f[][][]的第一维要两倍的n。

2.for()的时候要注意因为这里的i,j都是行,所以都要枚举到n,不要习惯性地写成n,m。

完整代码,炒鸡短。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int f[110][60][60];
int a[60][60];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++) scanf("%d", &a[i][j]);
    f[1][1][1] = a[1][1];
    for (int p = 2; p <= n + m - 1; p ++)
        for (int i = 1; i <= n && i <= p; i ++)
            for (int j = 1; j <= n && j <= p; j ++){
                if (i == 1 && j == 1) continue;
                f[p][i][j] = max(max(f[p - 1][i][j], f[p - 1][i - 1][j]), max(f[p - 1][i][j - 1], f[p - 1][i - 1][j - 1]));
                f[p][i][j] += i == j ? a[i][p - i + 1] : a[i][p - i + 1] + a[j][p - j + 1];
            }
    printf("%d\n", f[n + m - 1][n][n]);
    return 0;
}