AcWing 276. I-區域(狀態機 dp + Hard)

AcWing 276. I-區域

題目

給出 n 行 m 列矩陣,和一個參數 k,求 k 個格子組成的凸聯通塊最大權值和。(凸連通塊就是形狀是凸性的)。n < 30, m <

分析

此處凸包定義不嚴謹,可以直接理解成連續的若干行,每行的左端點列號先遞減、後遞增,右端點列號先遞增、後遞減。(這裏的遞增遞減都是不嚴格的)

那麼這樣就可以從每一行入手,考慮每行選取的起點和終點。

①: 狀態表示(經驗)
首先定義左右兩邊遞增狀態用 0 表示,遞減狀態用 1 表示。

  1. 集合:dp[i][j][L][R][x][y]dp[i][j][L][R][x][y] 表示所有選完前 i 行,且一共選了 j 個格子,第 i 行選取的左邊界是 L, 右邊界是 R,左邊界遞增遞減狀態是 x,右邊界遞增遞減狀態是 y 的連通塊的集合
  2. 屬性:表示集合中所有方案格子權值和最大值

②: 狀態轉移

對於每行的選取,左右邊界的遞增遞減狀態都有要求,那麼如何在選取過程中滿足這種要求?設計一個狀態機:
在這裏插入圖片描述
也就是遞減狀態只能由遞fdffd減推,而遞增狀態可以由遞減、遞增狀態推。這樣就滿足了題目要求(先遞減後遞增)。右邊界的自動機同理(先遞增後遞減)。

對於狀態 (x, y):
假如爲 (1, 0),只能由 (1, 0)轉移,即 (1, 0) --> (1, 0)
(1, 0), (1, 1) --> (1, 1)
(0, 0), (1, 0) --> (0, 0)
(0, 1), (1,1), (0, 0), (1, 0) —> (0, 1)

而同時考慮左右邊界 (L, R) 與上一行 (p, q) 的關係就有四種狀態:
在這裏插入圖片描述
總結一下:

  1. x=1,y=0x = 1, y = 0,則:
    f[i][j][l][r][x][y]=max(f[i1][j(rl+1)][p][q][1][0])+cost(i,l,r)f[i][j][l][r][x][y]=\max \left(f[i-1][j-(r-l+1)][p][q][1][0]\right)+\operatorname{cost}(i, l, r),其中:L<=p<=q<=RL <= p <= q <= R
  2. x=1,y=1x = 1, y = 1,則:
    f[i][j][l][r][x][y]=max(f[i1][j(rl+1)][p][q][0][0/1])+cost(i,l,r)f[i][j][l][r][x][y]=\max \left(f[i-1][j-(r-l+1)][p][q][0][0/1]\right)+\operatorname{cost}(i, l, r),其中:L<=p<=R<=qL <= p <= R <= q
  3. x=0,y=0x = 0, y = 0,則:
    f[i][j][l][r][x][y]=max(f[i1][j(rl+1)][p][q][0/1][0])+cost(i,l,r)f[i][j][l][r][x][y]=\max \left(f[i-1][j-(r-l+1)][p][q][0/1][0]\right)+\operatorname{cost}(i, l, r),其中:p<=L<=q<=Rp <= L <= q <= R
  4. x=0,y=1x = 0, y = 1,則:
    f[i][j][l][r][x][y]=max(f[i1][j(rl+1)][p][q][0/1][0/1])+cost(i,l,r)f[i][j][l][r][x][y]=\max \left(f[i-1][j-(r-l+1)][p][q][0/1][0/1]\right)+\operatorname{cost}(i, l, r),其中:p<=L<=R<=qp <= L <= R <= q
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 16;

int n, m, k;
int w[N][N];	// 每個點的權值
int dp[N][N * N][N][N][2][2];

struct State {
	int i, j, l, r, x, y;
} f[N][N * N][N][N][2][2];		// 保存狀態從什麼地方轉移過來

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

	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= k; j++)
			for (int l = 1; l <= m; l++)
				for (int r = l; r <= m; r++) {
					if (j < r - l + 1) continue;

					// 左擴張,右擴張(1,0)
					// 大括號爲了變量名不干擾
					{
						auto &vd = dp[i][j][l][r][1][0];
						auto &vf = f[i][j][l][r][1][0];
						for (int p = l; p <= r; p++) 
							for (int q = p; q <= r; q++) {
								int val = dp[i - 1][j - (r - l + 1)][p][q][1][0];
								if (vd < val) {
									vd = val;
									// 記錄路徑
									vf = {i - 1, j - (r - l + 1), p, q, 1, 0};
								}
							}
						for (int u = l; u <= r; u++) vd += w[i][u];
					}

					// 左擴張,右收縮(1,1)
					{
						auto &vd = dp[i][j][l][r][1][1];
						auto &vf = f[i][j][l][r][1][1];
						for (int p = l; p <= r; p++) 
							for (int q = r; q <= m; q++) 
								for (int y = 0; y < 2; y++) {
									int val = dp[i - 1][j - (r - l + 1)][p][q][1][y];
									if (vd < val) {
										vd = val;
										// 記錄路徑
										vf = {i - 1, j - (r - l + 1), p, q, 1, y};
									}
								}
						for (int u = l; u <= r; u++) vd += w[i][u];
					}

					// 左收縮,右擴張(0,0)
					{
						auto &vd = dp[i][j][l][r][0][0];
						auto &vf = f[i][j][l][r][0][0];
						for (int p = 1; p <= l; p++) 
							for (int q = l; q <= r; q++) 
								for (int x = 0; x < 2; x++) {
									int val = dp[i - 1][j - (r - l + 1)][p][q][x][0];
									if (vd < val) {
										vd = val;
										// 記錄路徑
										vf = {i - 1, j - (r - l + 1), p, q, x, 0};
									}
								}
						for (int u = l; u <= r; u++) vd += w[i][u];
					}

					// 左擴張,右擴張(0,1)
					{
						auto &vd = dp[i][j][l][r][0][1];
						auto &vf = f[i][j][l][r][0][1];
						for (int p = 1; p <= l; p++) 
							for (int q = r; q <= m; q++) 
								for (int x = 0; x < 2; x++)
									for (int y = 0; y < 2; y++) {
										int val = dp[i - 1][j - (r - l + 1)][p][q][x][y];
										if (vd < val) {
											vd = val;
											// 記錄路徑
											vf = {i - 1, j - (r - l + 1), p, q, x, y};
										}
									}
						for (int u = l; u <= r; u++) vd += w[i][u];
					}
				}
	int ans = 0;
	State state;
	for (int i = 1; i <= n; i++)
		for (int l = 1; l <= m; l++)
			for (int r = l; r <= m; r++)
				for (int x = 0; x < 2; x++) 
					for (int y = 0; y < 2; y++) {
						int val = dp[i][k][l][r][x][y];
						if (ans < val) {
							ans = val;
							state = {i, k, l, r, x, y};
						}
					} 
	printf("oil : %d\n", ans);

	while(state.j) {
		for (int i = state.l; i <= state.r; i++)
			printf("%d %d\n", state.i, i);
		state = f[state.i][state.j][state.l][state.r][state.x][state.y];
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章