5 ACwing 275 传纸条 题解
传纸条
题面
给定一个 \(N \times M\) 的矩阵 \(A\) ,每个格子中都有一个整数。现在需要找到两条从左上角 \((1, 1)\) 到右下角 \((N, M)\) 的路径,路径上的每一步只能向右或者向下走。路径经过的格子中的数会被取走,若两条路径同时经过一个格子,格子中的数只会被算一次,求取得的数最大是多少?
\(N,M \le 50\)
题解
首先确定 dp 的阶段,考虑路径形成的过程,我们需要从左上角开始一步一步考虑两条路径如何走,所以阶段就是当前走了几步
有一个比较好想出来的 dp 状态 \(f(x_1,y_1,x_2,y_x)\) 第一条路径走到了 \((x_1,y_1)\) ,第二条路径走到了 \((x_2,y_2)\) 的最大价值,时间复杂度 \(O(n^2m^2)\) ,因为 \(n,m\) 都比较小,所以也可以过
考虑能否减少冗余维度,发现 \(y_1,y_2\) 其实可以由 \(i\) 和 \(x_1,x_2\) 推出来,有以下等式
\[x_1+y_1 = x_2 + y_2 = i + 2
\]
于是我们可以只记 \(x_1,x_2\) ,设 \(f(i,x_1,x_2)\) 表示走了 \(i\) 步,第一条路径走到第 \(x_1\) 行,第二条路径走到了第 \(x_2\) 行,取数的最大价值
转移要考虑下一步两个位置会不会重合,如果重合,那么贡献只算一次,否则贡献算两次,然后四种情况分别转移即可,时间复杂度 \(O((n + m)n^2)\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 55;
int n, m;
int a[N][N];
//f[i][x1][x2] 表示走了 i 步,第一条路径在第 x1 行,第二条路径在第 x2 行的最大价值
int f[N << 1][N][N];
int main () {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n + m - 2; i++) {
for (int x1 = 1; x1 <= min (i + 1, n); x1 ++) {
for (int x2 = 1; x2 <= min (i + 1, n); x2 ++) {
int val = a[x1][i + 2 - x1];
/*
这里不用考虑有某个点路径1先经过,路径2后经过
因为两条路径到这个点的距离相同
只会出现同时经过、一个经过一个不经过、两个都不经过三种情况,不会出现一个先经过,另一个后经过
*/
if (x1 != x2) val += a[x2][i + 2 - x2];
auto &now = f[i][x1][x2];
now = max (now, f[i - 1][x1 - 1][x2 - 1] + val);
now = max (now, f[i - 1][x1 - 1][x2] + val);
now = max (now, f[i - 1][x1][x2 - 1] + val);
now = max (now, f[i - 1][x1][x2] + val);
}
}
}
cout << f[n + m - 2][n][n] << endl;
return 0;
}

浙公网安备 33010602011771号