P1004 NOIP2000 提高組 方格取數

jxyanglinus發表於2024-10-29

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;
}

相關文章