6 ACwing 276 i-country 题解
i-country
题面
给定一个 \(n\times m\) 的矩阵,每个点上有一个权值,要求寻找一个包含 \(K\) 个点的凸连通块(连通块中间没有空缺,并且轮廓是凸的),使得连通块中格子的权值和最大
注意:凸连通块是指:连续的若干行,每行的左端点列号先递减、后递增,右端点列号先递增、后递减。(递增、递减都是非严格的)
求出这个最大的权值和,并给出连通块的具体方案,输出任意一种方案即可。
题解
我们依次考虑每一行如何选择格子
需要关注的地方
- 当前处理到哪一行
- 当前用了多少格子
- 当前行的左端点、右端点
- 左边、右边分别是伸长还是缩减
行数和选择的格子可以作为 dp 的阶段,这两个阶段都满足线性增长的特点,所以设 \(f(i,j,l,r,x,y)\) 表示考虑前 \(i\) 行,选了 \(j\) 个格子,第 \(i\) 行的左右端点为 \(l,r\) ,左边右边的伸长/缩短状态为 \(x/y\)
状态转移,注意到,缩短可以从伸长转移过来,但伸长不可以从缩短转移过来。最终的目标状态是这四种状态中的任意一种,我卡了很长时间
- \(f(i,j,l,r,0,0) \to max\{f(i-1,j-(r-l+1),l',r',0,0)\}\)
- \(f(i,j,l,r,0,1)\to max\{f(i-1,j-(r-l+1),l',r',0,0/1) \}\)
- \(f(i,j,l,r,1,0)\to max\{f(i-1,j-(r-l+1),l',r',0/1,0) \}\)
- \(f(i,j,l,r,1,1)\to max\{f(i-1,j-(r-l+1),l',r',0/1,0/1) \}\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 20;
int n, m, k;
int f[N][N * N][N][N][2][2];
int a[N][N];
struct PRE {
int i, j, l, r, s, t, val;
} Pre[N][N * N][N][N][2][2];
/*
f[i][j][l][r][0/1][0/1]
表示考虑前 i 行,选了 j 个格子,
第 i 行从 l 选到 r,
左/右分别是 缩短/伸长 趋势
的最大价值
*/
int main () {
cin >> n >> m >> k;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
cin >> a[i][j];
}
}
//初始化 : 初始都为 0 即可,不会出现从不合法状态转移
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 <= 1; x ++) {
for (int y = 0; y <= 1; y ++) {
f[i][0][l][r][x][y] = f[i - 1][0][l][r][x][y];
}
}
}
}
*/
for (int j = 1; j <= k; j ++) {
for (int l = 1; l <= m; l ++) {
for (int r = l; r <= m; r ++) {
int len = r - l + 1;
if (len > j) continue;
//左伸长,右伸长
{
auto &now = f[i][j][l][r][1][1];
auto &pre = Pre[i][j][l][r][1][1];
for (int p = l; p <= r; p ++) {
for (int q = p; q <= r; q ++) {
auto val = f[i - 1][j - len][p][q][1][1];
if (now < val) {
now = val;
pre = {i - 1, j - len, p, q, 1, 1, 0};
}
}
}
for (int k = l; k <= r; k ++) {
now += a[i][k];
}
}
//左伸长,右缩短
{
auto &now = f[i][j][l][r][1][0];
auto &pre = Pre[i][j][l][r][1][0];
for (int p = l; p <= r; p ++) {
for (int q = r; q <= m; q ++) {
for (int y = 0; y <= 1; y ++) {
auto val = f[i - 1][j - len][p][q][1][y];
if (now < val) {
now = val;
pre = {i - 1, j - len, p, q, 1, y, 0};
}
}
}
}
for (int u = l; u <= r; u ++) {
now += a[i][u];
}
}
//左缩短,右伸长
{
auto &now = f[i][j][l][r][0][1];
auto &pre = Pre[i][j][l][r][0][1];
for (int p = 1; p <= l; p ++) {
for (int q = l; q <= r; q ++) {
for (int x = 0; x <= 1; x ++) {
auto val = f[i - 1][j - len][p][q][x][1];
if (now < val) {
now = val;
pre = {i - 1, j - len, p, q, x, 1, 0};
}
}
}
}
for (int u = l; u <= r; u ++) {
now += a[i][u];
}
}
//左缩短,右缩短
{
auto &now = f[i][j][l][r][0][0];
auto &pre = Pre[i][j][l][r][0][0];
for (int p = 1; p <= l; p ++) {
for (int q = r; q <= m; q ++) {
for (int x = 0; x <= 1; x ++) {
for (int y = 0; y <= 1; y ++) {
auto val = f[i - 1][j - len][p][q][x][y];
if (now < val) {
now = val;
pre = {i - 1, j - len, p, q, x, y, 0};
}
}
}
}
}
for (int u = l; u <= r; u ++) {
now += a[i][u];
}
}
}
}
}
}
PRE ans = {0, 0, 0, 0, 0, 0, 0};
for (int i = 1; i <= n; i++) {
for (int l = 1; l <= m; l ++) {
for (int r = 1; r <= m; r ++) {
for (int x = 0; x <= 1; x ++) {
for (int y = 0; y <= 1; y ++) {
if (ans.val < f[i][k][l][r][x][y]) {
ans = {i, k, l, r, x, y, f[i][k][l][r][x][y]};
}
}
}
}
}
}
printf ("Oil : %d\n", ans.val);
while (ans.i != 0 && ans.j != 0) {
for (int j = ans.l; j <= ans.r; j ++) {
printf ("%d %d\n", ans.i, j);
}
ans = Pre[ans.i][ans.j][ans.l][ans.r][ans.s][ans.t];
}
return 0;
}

浙公网安备 33010602011771号