题解 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;
}