洛谷 P1004 题解
洛谷 P1004
有 \(N \times N\) 的方格图,我们将某些格子中放入正整数,其他的格子放 \(0\)。
某人从方格图的左上角的 \(A\) 点出发,可以向右或者向下行走,直到达到右下角的 \(B\) 点。在走过的路上,他可以取走方格中的数(取走后方格中的数变成 \(0\))。
这个人总共走 \(2\) 次,请找出两条这样的路径,使得取出的数的和最大。
思路
首先,我们可以想到最暴力的暴力:搜两次。
但是,我们可以发现,因为只能往下和往右走,所以坐标有拓扑序,可以先搜一次,再做 dp。
但是,显然,我们分开做是会让时间复杂度变得很大的,所以,我们可以考虑一起做,也就是将状态变成 \((x_1, y_1, x_2, y_2)\),做 \(4\) 种转移,由于每次转移是两个点都走,所以,\(x_1 + y_1 = x_2 + y_2\) 是一直成立的,那么,只要 \(x_1 \ne x_2\),我们就可以将两个的格子的数都取走。
由于坐标有拓扑序,所以可以直接记忆化搜索和 dp,时间复杂度为 \(O(N ^ 4)\)。
前面我们提到过,\(x_1 + y_1 = x_2 + y_2\),所以,我们可以枚举 \(x, y\) 的和,再枚举 \(x_1, x_2\),得到 \(y_1, y_2\),进行转移,时间复杂度为 \(O(N ^ 3)\)。
代码
其实对于这道题除了搜两次以外,其他的都可以过,这里挂的 \(O(N ^ 3)\) 那种。
#include <iostream>
using namespace std;
const int N = 15;
const int dx[] = {0, 1};
const int dy[] = {1, 0};
int n, p[N][N], u, v, w, dp[2 * N][N][N];
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
while (cin >> u >> v >> w) {
if (!u && !v && !w) {
break;
}
p[u][v] = w;
}
int m = 2 * n;
for (int i = 2; i <= m; i++) {
for (int x1 = 1; x1 < i && x1 <= n; x1++) {
for (int x2 = 1; x2 < i && x2 <= n; x2++) {
int y1 = i - x1, y2 = i - x2, sum = 0;
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
int a = x1 - dx[j], c = x2 - dx[k];
sum = max(sum, dp[i - 1][a][c]);
}
}
dp[i][x1][x2] = sum + p[x1][y1] + p[x2][y2] - (x1 == x2 && y1 == y2) * p[x1][y1];
}
}
}
cout << dp[m][n][n];
return 0;
}