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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章