• 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) o f[i][j][l][r][1][0]+=f[i-1][0][0][0][1][0]$$

    $$if(j>r-l+1>0) o f[i][j][l][r][1][0]+=max{f[i-1][j-(r-l+1)][p][q][1][0]}(lle ple qle 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]}(0le yle 1)}(lle ple rle 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]}(0le xle 1)}(lle ple rle 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]}(0le yle 1)}(0le xle 1)}(lle ple rle 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
    ",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
    ",ans);
    44     print(ans1,ans2,ans3,ans4,ans5,ans6);
    45     return 0;
    46 }
    代码戳这里
  • 相关阅读:
    Silverligh OOB一直更新
    Silverlight使用通过服务绑定数据的控件
    vue prop不写value的处理逻辑
    .NET Core Http请求(GET、POST、上传文件并携带参数)
    流媒体服务器与web客户端插件的配置与搭建(Darwin Streaming server + Quictime plugin)
    目录
    margin标记可以带一个、二个、三个、四个参数,各有不同的含义。
    Asp.Net中几种相似数据绑定标记符号的解释及用法
    火狐自动换行 有空格
    xml
  • 原文地址:https://www.cnblogs.com/THWZF/p/11007484.html
Copyright © 2020-2023  润新知