在 N×M 的矩阵中,每个格子有一个权值,要求寻找一个包含 K 个格子的凸连通块(连通块中间没有空缺,并且轮廓是凸的),使这个连通块中的格子的权值和最大。
注意:凸连通块是指:连续的若干行,每行的左端点列号先递减、后递增,右端点列号先递增、后递减。
求出这个最大的权值和,并给出连通块的具体方案,输出任意一种方案即可。
输入格式
第一行包含三个整数N,M和K。
接下来N行每行M个整数,表示N*M的矩阵上每个格子的权值(均不超过1000)。
输出格式
第一行输出“Oil : X”,其中X为最大权值和。
接下来K行每行两个整数Xi和Yi,用来描述所有格子的具体位置,每个格子位于第Xi行,第Yi列。
数据范围
1≤N,M≤15
0≤K≤N∗M
输入样例:
2 3 4
10 20 30
40 2 3
输出样例:
Oil : 100
1 1
1 2
1 3
2 1
题解:
思路:逐行求解,跟随当前已经处理的数目, 还有当前行数的左右列坐标,还有趋势
(因为必须是凸的图形)
需要储存的状态:
- i 当前处理完的行数
- j 已经选出的格子数
- l 当前行已选格子的左端位置
- j 当前行已选格子的右端位置
- x 当前左侧轮廓单调类型
- y 当前右侧轮廓单调类型
记作f[i][j][l][r][x][y]
性质:
左右两条边单调性只会变化一次,即由扩张变为收缩
我们由第一行开始DP,如果有j<r-l+1
不合法的话,直接略过。
每次都要计算当前i,j,l,r的 左右扩张,左扩张与右收缩,左收缩与右扩张,左右收缩 四个状态。
我们用1来表示想左,0来表示向右
左右扩张:
左右扩张情况下,上面i-1行也必然是左右扩张,只需枚举出值最大的段再加上当前l->r的值就好了,即for (int p = l; p <= r; p++) for (int q = p; q <= r; q++) val=max(val,f[i - 1][j - (r - l + 1)][p][q][1][0])
需要注意的是当j==r-l+1
时,即当前行才是图形的第一行时,不需要计算上一行了
左扩张与右收缩:
这种情况下单调性已经变化过的边,i-1行会有两种可能性,即变化与未变化 要都枚举到
剩下的就不过多赘述了
…
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 16
int n,m,k;
int f[N][N*N][N][N][2][2];
int a[N][N];
struct node{
int i,j,l,r,x,y;
}g[N][N*N][N][N][2][2];
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
memset(f,-1,sizeof f);
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;
{
auto &vf=f[i][j][l][r][1][0];
auto &vg=g[i][j][l][r][1][0];
if(j==r-l+1) vf=0;
else {
for (int p = l; p <= r; p++)
for (int q = p; q <= r; q++) {
int val = f[i - 1][j - (r - l + 1)][p][q][1][0];
if (vf < val) {
vf = val;
vg = {i - 1, j - (r - l + 1), p, q, 1, 0};
}
}
}
for(int u=l;u<=r;u++) vf+=a[i][u];
}
{
auto &vf=f[i][j][l][r][1][1];
auto &vg=g[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<=1;y++){
int val=f[i-1][j-(r-l+1)][p][q][1][y];
if (vf < val) {
vf = val;
vg = {i - 1, j - (r - l + 1), p, q, 1, y};
}
}
for(int u=l;u<=r;u++) vf+=a[i][u];
}
{
auto &vf=f[i][j][l][r][0][0];
auto &vg=g[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<=1;x++){
int val=f[i-1][j-(r-l+1)][p][q][x][0];
if (vf < val) {
vf = val;
vg = {i - 1, j - (r - l + 1), p, q, x, 0};
}
}
for(int u=l;u<=r;u++) vf+=a[i][u];
}
{
auto &vf=f[i][j][l][r][0][1];
auto &vg=g[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<=1;x++)
for(int y=0;y<=1;y++) {
int val = f[i - 1][j - (r - l + 1)][p][q][x][y];
if (vf < val) {
vf = val;
vg = {i - 1, j - (r - l + 1), p, q, x, y};
}
}
for(int u=l;u<=r;u++) vf+=a[i][u];
}
}
int ans=0;
node t;
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 ++ ){
if(ans<f[i][k][l][r][x][y]){
ans=f[i][k][l][r][x][y];
t={i,k,l,r,x,y};
}
}
cout<<"Oil : "<<ans<<endl;
while(t.j){
for(int i=t.l;i<=t.r;i++) cout<<t.i<<' '<<i<<endl;
t=g[t.i][t.j][t.l][t.r][t.x][t.y];
}
return 0;
}