#线性动态规划#CH 5104 SGU 167 I-country

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/82109410

题目

在一个 n × m 的矩阵,找出一个含有k个格子的凸连通块(中间没有空缺),使权值最大,并输出方案


分析

既然要选出凸连通块且没有空缺,假定选择 l r 行,那么第 i 行( l i r )选择的区间 [ l i r i ] ( l i 1 l i l i + 1 , r i 1 r i r i + 1 ) ,然而已经很接近了,对于输出方案需要特判,在此不多讲
状态转移方程

f [ r o w + 1 ] [ l ] [ r ] [ 0 / 1 ( / ) ] [ 0 / 1 ( / ) ] [ ] = m a x ( f [ r o w ] [ l ] [ r ] [ 0 / 1 ] [ 0 / 1 ] [ k ] + i = l r g [ r o w ] [ i ] )
,然后初始化就是=前缀和


代码

#include <cstdio>
#include <cstring>
#define anns pre[mx][lx][rx][lp][rp][k]//存方案的地方(状态压缩)
int f[2][16][16][2][2][226],pre[16][16][16][2][2][226],n,m,k,s[16][16],ans,mxx,lxx,rxx,lpp,rpp;
int in(){//逐字符输入
    int ans=0; char c=getchar();
    while (c<48||c>57) c=getchar();
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans;
}
int max(int a,int b){return (a>b)?a:b;}
void update(int mx,int lx,int rx,int lp,int rp,int p){
    if (mx==n) return;//已经到了最后一层不需要更新
    for (register int nl=(lp?lx:1);nl<=rx;nl++)//左端点可能会凸起
    for (register int nr=max(lx,nl);nr<=(rp?m:rx);nr++){//右端点可能会凸起
        int t1,t2;
        if (nl==lx) t1=lp; else t1=nl>=lx;//是否存在凸起
        if (nr==rx) t2=rp; else t2=nr>=rx;
        if (f[mx&1^1][nl][nr][t1][t2][p+nr-nl+1]<f[mx&1][lx][rx][lp][rp][p]+s[mx+1][nr]-s[mx+1][nl-1]) //动态规划
            f[mx&1^1][nl][nr][t1][t2][p+nr-nl+1]=f[mx&1][lx][rx][lp][rp][p]+s[mx+1][nr]-s[mx+1][nl-1],
            pre[mx+1][nl][nr][t1][t2][p+nr-nl+1]=(lx<<12)+(rx<<8)+(lp<<4)+rp;//记录方案,状态压缩
    }
}
void dp(){
    for (register int mx=1;mx<=n;mx++){
    memset(f[mx&1^1],-1,sizeof(f[mx&1^1]));//初始化为-1
        for (register int lx=1;lx<=m;lx++)
        for (register int rx=lx;rx<=m;rx++)
        for (register int lp=0;lp<2;lp++)
        for (register int rp=0;rp<2;rp++){
            f[mx&1][lx][rx][lp][rp][rx-lx+1]=s[mx][rx]-s[mx][lx-1];//前缀和
            for (register int p=rx-lx+1;p<=k;p++)
            if (f[mx&1][lx][rx][lp][rp][p]>0){//存在答案
                update(mx,lx,rx,lp,rp,p);//更改
                if (p==k&&f[mx&1][lx][rx][lp][rp][p]>ans){//更大的答案
                    ans=f[mx&1][lx][rx][lp][rp][p];
                    mxx=mx; lxx=lx; rxx=rx; lpp=lp; rpp=rp;
                }
            }
        }
    }
}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}//逐字符输出
void write(int mx,int lx,int rx,int lp,int rp,int k){
    if (!mx||k<=0) return;//没有格子或者走到底退出
    write(mx-1,(anns>>12)%16,(anns>>8)%16,(anns>>4)%16,anns%16,k-rx+lx-1);//pre数组状态压缩
    for (register int i=lx;i<=rx;i++)
        print(mx),putchar(' '),print(i),putchar('\n');//输出方案
}
int main(){
    n=in(); m=in(); k=in();
    for (register int i=1;i<=n;i++)
    for (register int j=1;j<=m;j++) s[i][j]=s[i][j-1]+in();//前缀和
    memset(f[1],-1,sizeof(f[1]));//初始化为-1
    dp(); printf("Oil : %d\n",ans);
    write(mxx,lxx,rxx,lpp,rpp,k);//输出方案
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/82109410
sgu
167