P1004 NOIP2000 提高组 方格取数

P1004 NOIP2000 提高组 方格取数 - 洛谷

分析

与 [[小烈送菜]] 算姐妹题了,这个辈分甚至更老一点。

如果直接按照题目,从 \(A, B\) 两点分别出发,那么有个问题就是不确定性,计算的时候不可控因素很多。

可以注意到,从 \(B\) 点往回走到 \(A\) 点,是和从 \(A\) 点再走一遍走到 \(B\) 点是等价的。
那么这道题就可以转化为两个人从 \(A\) 点同时出发,路径可以交叉,求从方格中取数的最大总和。

接下来,分类讨论一下可能的情况。假设当前第一个人在 \((x1, y1)\),第二个人在 \((x2, y2)\),那么在下一步,会有以下四种情况:

  1. 同时向下走。
  2. 同时向右走。
  3. 第一个向下走,第二个向右走。
  4. 第一个向右走,第二个向下走。

同理,第一个人在 \((x1, y1)\),第二个人在 \((x2, y2)\) 的情况,也可以由以下四个位置到达:

  1. \((x1 - 1, y1), (x2 - 1, y2)\)
  2. \((x1, y1 - 1),(x2, y2 - 1)\)
  3. \((x1 - 1, y1),(x2, y2 - 1)\)
  4. \((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;
}
posted @ 2024-10-29 10:39  jxyanglinus  阅读(22)  评论(0)    收藏  举报