动态规划(DP):I-区域
题目:
在 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
思路:
一个凸连通块可以按行分为若干段连续区间[L,R],并且从上到下这些区间具有凸的性质(先向外扩张,再向内收缩,可以不完整),所以将DP的阶段设为每一行,一共n个阶段;然后考虑每个阶段内状态如何转移,对于一行中,需要关注的状态有当前选取的区间[L,R],当前区间收缩或扩张的趋势(依据这一点,每个阶段中分为四种情况,对于每一边都有如下的状态机)。
用F[i,j,l,r,x,y] (i表示当前行数,j表示当前已经划入凸连通块的块数,l表示当前行的左端点,r表示当前行的右端点,x表示左边界单调性类型,y表示右边界单调性类型)表示当前的状态。
我们可以得到状态转移方程:
至于选择的点:可以标记每步状态时如何转移过来的,递归求解或用栈求解
#include<bits/stdc++.h>
using namespace std;
const int N=17;
int a[N][N],sum[N][N];
int dp[N][N*N][N][N][2][2];
struct node{
int i,j,l,r,x,y;
}fa[N][N*N][N][N][2][2];
struct node mm;
void print(int i,int j,int l,int r,int x,int y)
{
if(j==0) return ;
//printf("%d %d %d %d %d %d\n",i,j,l,r,x,y);
struct node &now=fa[i][j][l][r][x][y];
print(now.i,now.j,now.l,now.r,now.x,now.y);
for(int k=l;k<=r;k++) printf("%d %d\n",i,k);
return ;
}
int main()
{
int n,m,k;
scanf("%d%d%d",&n,& m,&k);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
sum[i][j]=sum[i][j-1]+a[i][j];
}
}
int ans=0;
for(int i = 0; i <= n; i++)
{
for(int l = 1; l <= m; l++)
{
for(int r = l; r <= m; r++)
{
dp[i][0][l][r][0][0] = 0;
}
}
}
for(int i=1;i<=n;i++)
{
for(int l=1;l<=m;l++)
{
for(int r=l;r<=m;r++)
{
int s=sum[i][r]-sum[i][l-1];
for(int j=r-l+1;j<=k;j++)
{
//计算相关值
int ma00=0,ma01=0,ma10=0,ma11=0;
struct node fa00,fa01,fa10,fa11;
for(int p=l;p<=r;p++)
{
for(int q=p;q<=r;q++)
{
if(ma10<dp[i-1][j-(r-l+1)][p][q][1][0])
{
ma10=dp[i-1][j-(r-l+1)][p][q][1][0];
fa10={i-1,j-(r-l+1),p,q,1,0};
}
}
}
for(int p=l;p<=r;p++)
{
for(int q=r;q<=m;q++)
{
if(ma11<dp[i-1][j-(r-l+1)][p][q][1][1])
{
ma11=dp[i-1][j-(r-l+1)][p][q][1][1];
fa11={i-1,j-(r-l+1),p,q,1,1};
}
if(ma11<dp[i-1][j-(r-l+1)][p][q][1][0])
{
ma11=dp[i-1][j-(r-l+1)][p][q][1][0];
fa11={i-1,j-(r-l+1),p,q,1,0};
}
}
}
for(int p=1;p<=l;p++)
{
for(int q=l;q<=r;q++)
{
if(ma00<dp[i-1][j-(r-l+1)][p][q][1][0])
{
ma00=dp[i-1][j-(r-l+1)][p][q][1][0];
fa00={i-1,j-(r-l+1),p,q,1,0};
}
if(ma00<dp[i-1][j-(r-l+1)][p][q][0][0])
{
ma00=dp[i-1][j-(r-l+1)][p][q][0][0];
fa00={i-1,j-(r-l+1),p,q,0,0};
}
}
}
for(int p=1;p<=l;p++)
{
for(int q=r;q<=m;q++)
{
if(ma01<dp[i-1][j-(r-l+1)][p][q][1][0])
{
ma01=dp[i-1][j-(r-l+1)][p][q][1][0];
fa01={i-1,j-(r-l+1),p,q,1,0};
}
if(ma01<dp[i-1][j-(r-l+1)][p][q][0][0])
{
ma01=dp[i-1][j-(r-l+1)][p][q][0][0];
fa01={i-1,j-(r-l+1),p,q,0,0};
}
if(ma01<dp[i-1][j-(r-l+1)][p][q][1][1])
{
ma01=dp[i-1][j-(r-l+1)][p][q][1][1];
fa01={i-1,j-(r-l+1),p,q,1,1};
}
if(ma01<dp[i-1][j-(r-l+1)][p][q][0][1])
{
ma01=dp[i-1][j-(r-l+1)][p][q][0][1];
fa01={i-1,j-(r-l+1),p,q,0,1};
}
}
}
//转移
if(j==r-l+1)
{
dp[i][j][l][r][1][0]=s+dp[i-1][0][0][0][1][0];
fa[i][j][l][r][1][0]={i-1,0,0,0,1,0};
}
else
{
dp[i][j][l][r][1][0]=s+ma10;
fa[i][j][l][r][1][0]=fa10;
}
dp[i][j][l][r][1][1]=s+ma11;
fa[i][j][l][r][1][1]=fa11;
dp[i][j][l][r][0][0]=s+ma00;
fa[i][j][l][r][0][0]=fa00;
dp[i][j][l][r][0][1]=s+ma01;
fa[i][j][l][r][0][1]=fa01;
//更新答案
if(ans<=dp[i][j][l][r][1][0]) ans=dp[i][j][l][r][1][0],mm={i,j,l,r,1,0};
if(ans<=dp[i][j][l][r][1][1]) ans=dp[i][j][l][r][1][1],mm={i,j,l,r,1,1};
if(ans<=dp[i][j][l][r][0][0]) ans=dp[i][j][l][r][0][0],mm={i,j,l,r,0,0};
if(ans<=dp[i][j][l][r][0][1]) ans=dp[i][j][l][r][0][1],mm={i,j,l,r,0,1};
}
}
}
}
printf("Oil : %d\n",ans);
print(mm.i,mm.j,mm.l,mm.r,mm.x,mm.y);
}