bzoj 3144 [Hnoi2013]切糕

最小割。

题意简化一下就是:给一个矩阵,每个点给出一些带编号的选择,每个选择有权值,现在对于每个点做出一个选择,使得相邻点的选择的编号差小于等于d,并且使总权值最大。

首先考虑没有编号差小于等于d的限制,将一个点拆成选择个点,对于一个点的所有选择点,连成一条链,权值反应在边上,首尾与S,T相连,这样求一个最小割就是答案。

再考虑有了限制,某个点选了第i个选择,与它相邻的点就必须选在[i-d,i+d]中的选择。从当前点的i号点向相邻点的i-d号点连一条正无穷的边,再求最小割就好了。

这时如果割掉i的边,那么存在一条流从当前点i号点流向相邻点i-d号点,相邻点的i+d+1号点流向当前点的i+1号点,这样就保证选了i相邻点必须选[i-d,i+d]。

  1 #include
  2 #include
  3 #include
  4 #include
  5 #include
  6 using namespace std;
  7 const int dian=70005;
  8 const int bian=500005;
  9 const int INF=0x3f3f3f3f;
 10 int h[dian],nxt[bian],ver[bian],val[bian],ch[dian],cr[dian];
 11 int n,m,hh,tot,d,aa;
 12 int S,T;
 13 int bh(int a,int b,int c){
 14     return (a-1)*m*hh+(b-1)*hh+c;
 15 }
 16 void add(int a,int b,int c){
 17     tot++;ver[tot]=b;val[tot]=c;nxt[tot]=h[a];h[a]=tot;
 18     tot++;ver[tot]=a;val[tot]=0;nxt[tot]=h[b];h[b]=tot;
 19 }
 20 bool tell(){
 21     memset(ch,-1,sizeof(ch));
 22     queue<int>q;
 23     q.push(S);
 24     ch[S]=0;
 25     while(!q.empty()){
 26         int t=q.front();
 27         q.pop();
 28         for(int i=h[t];i;i=nxt[i])
 29             if(ch[ver[i]]==-1&&val[i]){
 30                 ch[ver[i]]=ch[t]+1;
 31                 q.push(ver[i]);
 32             }
 33     }
 34     return ch[T]!=-1;
 35 }
 36 int zeng(int a,int b){
 37     if(a==T)
 38         return b;
 39     int r=0;
 40     for(int i=cr[a];i&&b>r;i=nxt[i])
 41         if(ch[ver[i]]==ch[a]+1&&val[i]){
 42             int t=zeng(ver[i],min(b-r,val[i]));
 43             val[i]-=t,r+=t,val[i^1]+=t;
 44             if(val[i])
 45                 cr[a]=i;
 46         }
 47     if(!r)
 48         ch[a]=-1;
 49     return r;
 50 }
 51 int dinic(){
 52     int r=0,t;
 53     while(tell()){
 54         for(int i=1;i<=n*m*hh+2;i++)
 55             cr[i]=h[i];
 56         while(t=zeng(S,INF))
 57             r+=t;
 58     }
 59     return r;
 60 }
 61 int main(){
 62     memset(h,0,sizeof(h));
 63     memset(nxt,0,sizeof(nxt));
 64     tot=1;
 65     scanf("%d%d%d",&n,&m,&hh);
 66     S=n*m*hh+1,T=n*m*hh+2;
 67     for(int i=1;i<=n;i++)
 68         for(int j=1;j<=m;j++)
 69             add(S,bh(i,j,1),INF);
 70     scanf("%d",&d);
 71     for(int i=1;i<=hh;i++)
 72         for(int j=1;j<=n;j++)
 73             for(int k=1;k<=m;k++){
 74                 scanf("%d",&aa);
 75                 if(i!=hh)
 76                     add(bh(j,k,i),bh(j,k,i+1),aa);
 77                 else
 78                     add(bh(j,k,i),T,aa);
 79             }
 80     for(int i=1;i<=n;i++)
 81         for(int j=1;j<=m;j++){
 82             if(i-1>=1){
 83                 for(int k=1;k<=d;k++)
 84                     add(bh(i,j,k),bh(i-1,j,1),INF);
 85                 for(int k=d+1;k<=hh;k++)
 86                     add(bh(i,j,k),bh(i-1,j,k-d),INF);
 87             }
 88             if(i+1<=n){
 89                 for(int k=1;k<=d;k++)
 90                     add(bh(i,j,k),bh(i+1,j,1),INF);
 91                 for(int k=d+1;k<=hh;k++)
 92                     add(bh(i,j,k),bh(i+1,j,k-d),INF);
 93             }
 94             if(j-1>=1){
 95                 for(int k=1;k<=d;k++)
 96                     add(bh(i,j,k),bh(i,j-1,1),INF);
 97                 for(int k=d+1;k<=hh;k++)
 98                     add(bh(i,j,k),bh(i,j-1,k-d),INF);
 99             }
100             if(j+1<=m){
101                 for(int k=1;k<=d;k++)
102                     add(bh(i,j,k),bh(i,j+1,1),INF);
103                 for(int k=d+1;k<=hh;k++)
104                     add(bh(i,j,k),bh(i,j+1,k-d),INF);
105             }
106         }
107     printf("%d",dinic());
108     return 0;
109 }

 

你可能感兴趣的:(bzoj 3144 [Hnoi2013]切糕)