AcWing 275. 傳紙條 (dp 狀態優化)

AcWing 275. 傳紙條

題目

給一個 m 行 n 列矩陣,當前位置在(1,1),需要走到(m,n)然後再走回來,走到一個點可以拿走當前的值,但是每個點只能走一次,求最後能得到的最大值。

分析

如果只有一條路線就很簡單了。現在要回去,可以看成同時從(1,1)走兩條不相交的路線,狀態表示可以用 dp[x1][y1][x2][y2]dp[x1][y1][x2][y2] 從 (1,1) 分別走到兩個點。

進一步優化狀態,可以記錄走了 k 步,和兩點的 x 座標。

①: 狀態表示(經驗)

  1. 集合:dp[k][x1][x2]dp[k][x1][x2] 表示所有兩條路線從 (1,1) 分別走到 (x1,kx1),(x2,kx2)(x1, k-x1), (x2, k-x2)的所有方案(不相交)
  2. 屬性:表示集合中所有方案路徑花費之和的最大值

②: 狀態轉移

兩條路線分別可以從右、下走到當前位置 dp[k][x1][x2]dp[k][x1][x2],排列組合一下就是 4 種方式。

推廣:如果這個題要走 k 條路徑的話,就不能用 dp 來做了,狀態太多,要用最小費用最大流

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 50 + 5;

int n, m, t;
int w[N][N];
int dp[N * 2][N][N];

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++) 
			scanf("%d", &w[i][j]);

	for (int k = 2; k <= n + m; k++) 
		for (int x1 = max(1, k - m); x1 <= min(k - 1, n); x1++) 
			for (int x2 = max(1, k - m); x2 <= min(k - 1, n); x2++) {
				if (x1 == x2) continue;     // 不能走相同的點
				if (x1 != x2) t = w[x1][k - x1] + w[x2][k - x2];
				for (int a = 0; a < 2; a++) 		// 四個方向
					for (int b = 0; b < 2; b++)
						dp[k][x1][x2] = max(dp[k][x1][x2], dp[k - 1][x1 - a][x2 - b] + t);
			}
	// 最後一個點的值 加上 兩個方向來的最大值
	printf("%d\n", w[n][m] + max(dp[n + m - 1][n][n - 1], dp[n + m - 1][n - 1][n]));
	return 0;
}


代碼其實也可以寫成,其中如果兩人在相同格子,則 t 等於這個格子的分值;否則等於兩個格子的分值之和。主要是爲了計算最後一個點,中間走相同點的肯定會被其他值替換,所以沒事。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 50 + 5;

int n, m;
int w[N][N];
int dp[N * 2][N][N];

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++) 
			scanf("%d", &w[i][j]);

	for (int k = 2; k <= n + m; k++) 
		for (int x1 = max(1, k - m); x1 <= min(k - 1, n); x1++) 
			for (int x2 = max(1, k - m); x2 <= min(k - 1, n); x2++) {
				int t = w[x1][k - x1];
				if (x1 != x2) t += w[x2][k - x2];
				for (int a = 0; a < 2; a++) 		// 四個方向
					for (int b = 0; b < 2; b++)
						dp[k][x1][x2] = max(dp[k][x1][x2], dp[k - 1][x1 - a][x2 - b] + t);
			}
	printf("%d\n", dp[n + m][n][n]);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章