分析
与 [[小烈送菜]] 算姐妹题了,这个辈分甚至更老一点。
如果直接按照题目,从 \(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;
}
标签:x1,NOIP2000,int,P1004,取数,x2,maxn,y1,y2
From: https://www.cnblogs.com/jxyanglinus/p/18512427