P1004 NOIP2000 提高組 方格取數 - 洛谷
分析
與 [[小烈送菜]] 算姐妹題了,這個輩分甚至更老一點。
如果直接按照題目,從 \(A, B\) 兩點分別出發,那麼有個問題就是不確定性,計算的時候不可控因素很多。
可以注意到,從 \(B\) 點往回走到 \(A\) 點,是和從 \(A\) 點再走一遍走到 \(B\) 點是等價的。
那麼這道題就可以轉化為兩個人從 \(A\) 點同時出發,路徑可以交叉,求從方格中取數的最大總和。
接下來,分類討論一下可能的情況。假設當前第一個人在 \((x1, y1)\),第二個人在 \((x2, y2)\),那麼在下一步,會有以下四種情況:
- 同時向下走。
- 同時向右走。
- 第一個向下走,第二個向右走。
- 第一個向右走,第二個向下走。
同理,第一個人在 \((x1, y1)\),第二個人在 \((x2, y2)\) 的情況,也可以由以下四個位置到達:
- \((x1 - 1, y1), (x2 - 1, y2)\)
- \((x1, y1 - 1),(x2, y2 - 1)\)
- \((x1 - 1, y1),(x2, y2 - 1)\)
- \((x1, y1 - 1),(x2 - 1, y2)\)
分析到這個地方,這題已經很好做了。根據上面的第一種討論,可以寫出一個記憶化搜尋;而根據第二種討論,很容易寫出一種四維 DP。
另外,根據題意,同一個方格最多隻能取一次,所以要判斷座標重疊的情況。
程式碼
DP 寫法
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10;
int n;
int graph[maxn][maxn];
int f[maxn][maxn][maxn][maxn];
int main() {
scanf("%d", &n);
while (true) {
int x, y, val;
scanf("%d%d%d", &x, &y, &val);
if (x == 0 && y == 0 && val == 0) break;
graph[x][y] = val;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= n; k++) {
for (int l = 1; l <= n; l++) {
f[i][j][k][l] = max(f[i][j][k][l], f[i - 1][j][k - 1][l] + graph[i][j] + graph[k][l] * (i != k || j != l)); // 座標判重,相同座標只計算一次。
f[i][j][k][l] = max(f[i][j][k][l], f[i][j - 1][k][l - 1] + graph[i][j] + graph[k][l] * (i != k || j != l));
f[i][j][k][l] = max(f[i][j][k][l], f[i - 1][j][k][l - 1] + graph[i][j] + graph[k][l] * (i != k || j != l));
f[i][j][k][l] = max(f[i][j][k][l], f[i][j - 1][k - 1][l] + graph[i][j] + graph[k][l] * (i != k || j != l));
}
}
}
}
printf("%d", f[n][n][n][n]);
return 0;
}
記憶化搜尋
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10;
int n;
int f[maxn][maxn][maxn][maxn];
int val[maxn][maxn];
inline bool inArea(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= n;
}
int dx1[4] = {1, 0, 1, 0};
int dy1[4] = {0, 1, 0, 1};
int dx2[4] = {1, 0, 0, 1};
int dy2[4] = {0, 1, 1, 0};
int dfs(int _x1, int _y1, int _x2, int _y2) {
if (f[_x1][_y1][_x2][_y2] != -1) return f[_x1][_y1][_x2][_y2];
if (_x1 == n && _y1 == n && _x2 == n && _y2 == n) return 0;
int maxv = -1;
for (int i = 0; i < 4; i++) {
int vx1 = _x1 + dx1[i], vy1 = _y1 + dy1[i];
int vx2 = _x2 + dx2[i], vy2 = _y2 + dy2[i];
if (!inArea(vx1, vy1) || !inArea(vx2, vy2)) continue;
maxv = max(maxv, dfs(vx1, vy1, vx2, vy2) + val[vx1][vy1] + val[vx2][vy2] * (vx1 != vx2 || vy1 != vy2));
}
f[_x1][_y1][_x2][_y2] = maxv;
return f[_x1][_y1][_x2][_y2];
}
int main() {
scanf("%d", &n);
memset(f, -1, sizeof(f)); // 注意初始化為 -1, 因為可能出現取的數為 0 的情況
while (true) {
int x, y, v;
scanf("%d%d%d", &x, &y, &v);
if (x == 0 && y == 0 && v == 0) break;
val[x][y] = v;
}
dfs(1, 1, 1, 1);
printf("%d", f[1][1][1][1] + val[1][1]); // 搜尋時沒有算 (1, 1),在這裡加上
return 0;
}