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 }
代码戳这里
posted @ 2019-06-12 08:43  小叽居biubiu  阅读(322)  评论(0编辑  收藏  举报