zoj 3475 最小割 巧妙建图

第一次接触这种类型的建图题,记录一下

题意:在一个20*20的方格中,建造一些围墙,使得X和某些A与E和外界都不联通,建造围墙有对应的花费,而对于满足条件的A,有对应的收入。

首先2^5枚举哪些A要被围住,然后就要求所需的最小费用,这就是典型的最大流最小割问题。其中X和所枚举的A同源连一条流量为inf的边,E和边界同汇连一条流量为inf的边。

以上是watashi的简练题解,我等若菜一开始还没反应过来

为什么建好图后的最小割就是把X和一些A与E隔开的最小代价呢?注意,选择了某条边为割边就表示方格中两个格子间的那条围墙被选择了。

注意到S集合是X和A,T集合是E和边界,所以求一下最小割后s集合中的点无法到达T集合了,

因此也就完成了把X和一些A围起来且与E隔开的功能

 

View Code
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
const int MAX=405;
const int INF=1000000000;
struct{
    int v,c,next;
}edge[2000],EDGE[2000];
int E,head[MAX],S,T;
int gap[MAX],cur[MAX];
int pre[MAX],dis[MAX];
void add_edge(int s,int t,int c){
    edge[E].v=t; edge[E].c=c;
    edge[E].next=head[s];
    head[s]=E++;
    edge[E].v=s; edge[E].c=c;
    edge[E].next=head[t];
    head[t]=E++;
}
int min(int a,int b){return (a==-1||b<a)?b:a;}
int SAP(int s,int t,int n){
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    int i;
    for(i=0;i<n;i++)cur[i]=head[i];
    int u=pre[s]=s,maxflow=0,aug=-1,v;
    gap[0]=n;    
    while(dis[s]<n){
loop:    for(i=cur[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            if(edge[i].c>0&&dis[u]==dis[v]+1){
                aug=min(aug,edge[i].c);
                pre[v]=u;
                cur[u]=i;
                u=v;
                if(u==t){
                    for(u=pre[u];v!=s;v=u,u=pre[u]){
                        edge[cur[u]].c-=aug;
                        edge[cur[u]^1].c+=aug;
                    }
                    maxflow+=aug;
                    aug=-1;
                }
                goto loop;
            }
        }
        int mindis=n;
        for(i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            if(edge[i].c>0&&dis[v]<mindis){
                cur[u]=i;
                mindis=dis[v];
            }
        }
        if((--gap[dis[u]])==0)break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];
    }
    return maxflow;
}
int x[MAX],y[MAX],z[MAX],H[MAX],n,m;
int ID(int x,int y){return x*m+y;}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
         S=n*m;T=n*m+1;
         memset(head,-1,sizeof(head));
         E=0;
         for(int i=0,f;i<=n;i++){
             for(int j=0;j<m;j++){
                 scanf("%d",&f);
                 add_edge(i==0 ? T : ID(i-1,j),i==n ? T : ID(i,j),f);
             }
             if(i==n) break;
             for(int j=0,f;j<=m;j++){
                 scanf("%d",&f);
                 add_edge(j==0 ? T : ID(i,j-1) , j==m ? T : ID(i,j),f);
             }
         }
         int k;
         scanf("%d",&k);int t=k;
         for(int i=0;i<t;i++){
             scanf("%d%d%d",&z[i],&x[i],&y[i]);
             if(z[i]==0){
                 swap(z[i],z[0]);
                 swap(y[i],y[0]);
                 swap(x[i],x[0]);
             }
             else if(z[i]<0){
                 add_edge(ID(x[i],y[i]),T,INF);
                 i--;t--;
             }
         }
         memcpy(EDGE,edge,sizeof(edge));
         memcpy(H,head,sizeof(head));
         int ans=INF;
         int tmp=E;
         for(int i=1,f=0;i<(1<<t);i+=2){
             f=0;
             memcpy(head,H,sizeof(H));
             memcpy(edge,EDGE,sizeof(EDGE));E=tmp;
             for(int j=0;j<t;j++){
                 if(i&(1<<j)){
                     add_edge(S,ID(x[j],y[j]),INF);
                     f-=z[j];
                 }
                
             }
             f+=SAP(S,T,n*m+2);
             ans=min(ans,f);
         }
         printf("%d\n",ans);
    }
    return 0;
}

 

 

 

posted @ 2012-07-12 23:26  Because Of You  Views(677)  Comments(0Edit  收藏  举报