然而它并不easy
题目给你一个 n ∗ m n*m n∗m 的矩阵,让后你可以选一个占 k k k 个格子的凸连通块(中间没有空隙),每个格子有一定的值,问你选择的范围内所有格子的总和最为多少
这道题刚碰到你会感觉没有思路,需要推下样例(或者最好是自己的小数据,因为样例有点水),然后你会找到一个规律,即上下两行之间的关系(或者左右两行的,但这里我以行为例来讲)
显然一个凸多边形可以按行分为若干段连续的区间 [ l , r ] [l,r] [l,r],而且左端点的值先减后增,右端点先增后减
所以我们可以将阶段设置成每一行,一共需要n个阶段
除此之外,在状态转移中,我们还需要记录已经选的格子数,当前的区间,以及左右端点是在递增还是递减,即用 f [ i ] [ j ] [ l ] [ r ] [ x ] [ y ] f[i][j][l][r][x][y] f[i][j][l][r][x][y] 表示当前行,已选格子数,左端点,右端点,左端点递增或减,右端点增或减(0表示向内缩小,1表示向外扩大,即左端点1为减0为增,右端点0为增1为减,现在看来这样貌似在自找麻烦)
然后就一共有4种情况讨论(左右端点分别为递增还是递减)
而要实现方案输出,可以用额外的数组来记录当前状态是由哪个状态转移过来的,最后从结尾一次递归即可
#include
using namespace std;
int n,m,k,l,r,x,y,i,j,a[20][20],b[20][20],f[20][250][20][20][2][2];
int nl[20][250][20][20][2][2],nr[20][250][20][20][2][2];
int nx[20][250][20][20][2][2],ny[20][250][20][20][2][2];
//这里的数组你可以开成一个结构体,因为明显我现在发现这个太烦了(然而这个既然敲着了,那也就不改了)
void init(){
scanf("%d%d%d",&n,&m,&k);
memset(f,0,sizeof(f));
for(i=1;i<=n;++i)
for(j=1;j<=m;++j){
scanf("%d",&a[i][j]);
b[i][j]=b[i][j-1]+a[i][j];//求个前缀和方便求区间和
}
}
void gg(int p,int L,int R,int X,int Y){//记录当前状态是由哪个状态转移过来的且保证最优方案
if(p<f[i][j][l][r][x][y]) return;
f[i][j][l][r][x][y]=p;
nl[i][j][l][r][x][y]=L;
nr[i][j][l][r][x][y]=R;
nx[i][j][l][r][x][y]=X;
ny[i][j][l][r][x][y]=Y;
}
void work(){
for(i=1;i<=n;++i)
for(j=1;j<=k;++j)
for(l=1;l<=m;++l)
for(r=l;r<=m;++r){
int len=r-l+1,p;
if(len>j) break;//当前行上的格子数大于所要的全部格子数,直接进下一种情况
p=b[i][r]-b[i][l-1];
for(x=0;x<=1;++x)
for(y=0;y<=1;++y)
gg(p,m,0,x,y);
x=y=1;//左减右增只能从左减右增状态转移过来
for(int h=l;h<=r;++h)
for(int g=h;g<=r;++g)
gg(f[i-1][j-len][h][g][1][1]+p,h,g,1,1);
x=1;
y=0;//左减右减可能从左减右增状态或左减右减状态转移过来
for(int h=l;h<=r;++h)
for(int g=r;g<=m;++g){
gg(f[i-1][j-len][h][g][1][0]+p,h,g,1,0);
gg(f[i-1][j-len][h][g][1][1]+p,h,g,1,1);
}
x=0;
y=1;//左增右增可能从左减右增状态或左增右增状态转移过来
for(int h=1;h<=l;++h)
for(int g=l;g<=r;++g){
gg(f[i-1][j-len][h][g][0][1]+p,h,g,0,1);
gg(f[i-1][j-len][h][g][1][1]+p,h,g,1,1);
}
x=y=0;//左增右减可能从任何一种状态转移过来
for(int h=1;h<=l;++h)
for(int g=r;g<=m;++g){
gg(f[i-1][j-len][h][g][0][0]+p,h,g,0,0);
gg(f[i-1][j-len][h][g][0][1]+p,h,g,0,1);
gg(f[i-1][j-len][h][g][1][0]+p,h,g,1,0);
gg(f[i-1][j-len][h][g][1][1]+p,h,g,1,1);
}
}
}
void hh(int p,int k,int l,int r,int x,int y){
if(!k) return;
hh(p-1,k-(r-l+1),nl[p][k][l][r][x][y],nr[p][k][l][r][x][y],nx[p][k][l][r][x][y],ny[p][k][l][r][x][y]);
for(int h=l;h<=r;++h)
printf("%d %d\n",p,h);
}
void prin(){
int p,el,er,ex,ey,sum=0;
for(int h=1;h<=n;++h)
for(l=1;l<=m;++l)
for(r=l;r<=m;++r)
for(x=0;x<=1;++x)
for(y=0;y<=1;++y)
if(sum<f[h][k][l][r][x][y]){
sum=f[h][k][l][r][x][y];
p=h; el=l; er=r; ex=x; ey=y;
}
printf("Oil : %d\n",sum);
hh(p,k,el,er,ex,ey);
}
int main(){
freopen("input.in" ,"r",stdin );
freopen("output.out","w",stdout);
init();
work();
prin();
return 0;
}