题目链接:https://www.acwing.com/problem/content/278/
题目给出一个n*m的矩阵,要求从中找出一个凸连通块,使得该连通块包含k个格子并且格子数之和最大,凸连通块由若干行构成,左右最多各有一个峰值。
可通过DP解决,每行处理完代表一个阶段,f[i,j,l,r,x,y]表示当前处理第i行,一共选择了j个格子,第i行选择的格子是[l,r],左右的状态是x,y表示下降或者上升。转移时要枚举每一种状态,并且枚举上一个阶段的左右位置。找到最大的一个,在加上第i行的静态的[l,r]的和。
代码:
#include<iostream> #include<cstdio> using namespace std; const int maxn = 16; int f[maxn][maxn*maxn][maxn][maxn][2][2]; struct State{//保存行数,格子数,左端点右端点,上升和递减的状态 int i,j,l,r,x,y; }g[maxn][maxn*maxn][maxn][maxn][2][2]; int n,m,k; int w[maxn][maxn]; int main(){ cin>>n>>m>>k; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>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; //左边扩张,右边扩张 { int& vf=f[i][j][l][r][1][0]; State& vg=g[i][j][l][r][1][0]; //找到从上一个状态扩展来的最大的一个状态 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+=w[i][u]; } //左边收缩,右边扩张 { int &vf=f[i][j][l][r][0][0]; State &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+=w[i][u]; } //左边收缩,右边收缩 { int &vf=f[i][j][l][r][0][1]; State &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+=w[i][u]; } //左边扩张,右边收缩 { int &vf=f[i][j][l][r][1][1]; State &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+=w[i][u]; } } int res=0; State state; //查找最大的终态 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(f[i][k][l][r][x][y]>res){ res=f[i][k][l][r][x][y]; state={i,k,l,r,x,y}; } printf("Oil : %d ",res); while(state.j){//该状态中还有没算到的格子 for(int i=state.l;i<=state.r;i++) printf("%d %d ",state.i,i); state=g[state.i][state.j][state.l][state.r][state.x][state.y]; } return 0; }