I-country 题解报告
【题目大意】
在n*m的矩阵中,每个格子都有一个权值a,求一个包含k个格子的凸连通块,使这个连通块中的格子的权值和最大。
【思路分析】
任何一个凸连通块都可以分成连续的若干行,每行的左端点列号先递减后递增,右端点列号先递增后递减。我们可以依次考虑从n*m矩阵中的每一行中选择哪些格子来构成所求的凸连通块,那么我们需要关注的信息有:
1.当前已经处理完的行数i
2.已经选出的格子数j
3.当前行已选格子的左端位置l——为了确定下一行左端点的范围,以满足单调性
4.当前行已选格子的右端位置r——为了确定下一行右端点的范围,以满足单调性
5.当前左侧轮廓的单调性类型x——递增(0)还是递减(1)
6.当前右侧轮廓的单调性类型y——递增(0)还是递减(1)
于是我们设f[i][j][l][k][x][y]表示前i行选了j个格子,其中第i行选择了第l到r个格子,左边界单调性为x,右边界单调性为y时,构成的凸连通块的最大权值之和。
接着可以列出状态转移方程:
1.左减右增
$$f[i][j][l][r][1][0]=\sum_{p=l}^{r}a[i][p]$$
$$if(j=r-l+1>0)\to f[i][j][l][r][1][0]+=f[i-1][0][0][0][1][0]$$
$$if(j>r-l+1>0)\to f[i][j][l][r][1][0]+=max\{f[i-1][j-(r-l+1)][p][q][1][0]\}(l\le p\le q\le r)$$
2.左减右减
$$f[i][j][l][r][1][1]=\sum_{p=l}^{r}a[i][p]+max\{max\{f[i-1][j-(r-l+1)][p][q][1][y]\}(0\le y\le 1)\}(l\le p\le r\le q)$$
3.左增右增
$$f[i][j][l][r][0][0]=\sum_{p=l}^{r}a[i][p]+max\{max\{f[i-1][j-(r-l+1)][p][q][x][0]\}(0\le x\le 1)\}(l\le p\le r\le q)$$
4.左增右减
$$f[i][j][l][r][0][1]=\sum_{p=l}^{r}a[i][p]+max\{max\{max\{f[i-1][j-(r-l+1)][p][q][x][y]\}(0\le y\le 1)\}(0\le x\le 1)\}(l\le p\le r\le q)$$
初始值:f[i][0][0][0][1][0]=0
目标:$max\{f[i][k][l][r][x][y]\}$
因为本题还要输出方案,所以我们可以用一个数组在DP过程中记录最优解是由哪一个状态转移过来的,这样就可以在最后递归一下得到方案了。
【代码实现】
1 #include<bits/stdc++.h> 2 #define rg register 3 #define go(i,a,b) for(rg int i=a;i<=b;i++) 4 #define back(i,a,b) for(rg int i=a;i>=b;i--) 5 using namespace std; 6 int n,m,k,ans=0; 7 int a[16][16],f[16][226][16][16][2][2]; 8 struct point{ 9 int l,r,x,y; 10 }tag[16][226][16][16][2][2]; 11 int ans1,ans2,ans3,ans4,ans5,ans6; 12 void print(int a1,int a2,int a3,int a4,int a5,int a6){ 13 if(!a1||!a2) return; 14 print(a1-1,a2-(a4-a3+1),tag[a1][a2][a3][a4][a5][a6].l,tag[a1][a2][a3][a4][a5][a6].r,tag[a1][a2][a3][a4][a5][a6].x,tag[a1][a2][a3][a4][a5][a6].y); 15 go(i,a3,a4) 16 printf("%d %d\n",a1,i); 17 return; 18 } 19 int main(){ 20 scanf("%d%d%d",&n,&m,&k); 21 go(i,1,n)go(j,1,m) scanf("%d",&a[i][j]),a[i][j]+=a[i][j-1]; 22 go(i,1,n) go(j,1,k) go(l,1,m) go(r,l,m){ 23 int sum=a[i][r]-a[i][l-1],len=r-l+1; 24 if(j<len) break; 25 //注意循环边界的变化 26 go(p,l,r) go(q,p,r) 27 if(f[i][j][l][r][1][0]<sum+f[i-1][j-len][p][q][1][0]) 28 f[i][j][l][r][1][0]=sum+f[i-1][j-len][p][q][1][0],tag[i][j][l][r][1][0]=(point){p,q,1,0}; 29 go(p,l,r) go(q,r,m) go(y,0,1) 30 if(f[i][j][l][r][1][1]<sum+f[i-1][j-len][p][q][1][y]) 31 f[i][j][l][r][1][1]=sum+f[i-1][j-len][p][q][1][y],tag[i][j][l][r][1][1]=(point){p,q,1,y}; 32 go(p,1,l) go(q,l,r) go(x,0,1) 33 if(f[i][j][l][r][0][0]<sum+f[i-1][j-len][p][q][x][0]) 34 f[i][j][l][r][0][0]=sum+f[i-1][j-len][p][q][x][0],tag[i][j][l][r][0][0]=(point){p,q,x,0}; 35 go(p,1,l) go(q,r,m) go(x,0,1) go(y,0,1) 36 if(f[i][j][l][r][0][1]<sum+f[i-1][j-len][p][q][x][y]) 37 f[i][j][l][r][0][1]=sum+f[i-1][j-len][p][q][x][y],tag[i][j][l][r][0][1]=(point){p,q,x,y}; 38 if(j!=k) continue; 39 go(x,0,1) go(y,0,1) 40 if(ans<f[i][j][l][r][x][y]) 41 ans=f[i][j][l][r][x][y],ans1=i,ans2=j,ans3=l,ans4=r,ans5=x,ans6=y; 42 } 43 printf("Oil : %d\n",ans); 44 print(ans1,ans2,ans3,ans4,ans5,ans6); 45 return 0; 46 }