有上下界的网络流

解决上下界网络流的一般思路:

  解决这类问题的关键是如何去掉下界带来的麻烦。下面的哈工大出版的《图论及应用》里的思路。

1、网络的必要弧和构建附加网络

 

前一个数是下界,后一个数是上界。

 

 

下面的边是必要弧,其权值为下界。上面的边容量为上界与下界的差。

 

 

添加附加源点Y,附加汇点X (别弄错了) <X , Y>为的权值正无穷。对于必要弧<u , v>,添加<u,Y>,<Y,v>,容量为必要弧的容量。

 

 

删除<X, Y>,添加<T, S>,容量为正无穷。这样必要弧满流转化为最大流能够占满与Y相连的所有的边,同样占满所有与X相连的边。

 最后这一幅图就是我们解这类问题的构图方法了。对照题目,可以发现,这种题目的构图方式几乎定死了。

 

2、解题的步骤

(1)定义数据结构

需要最大流的模版,存图方式一样(链式前向星),容量c = up - b (上界减去下界)

定义low[]记录边的容量下界。 

定义两个数组out[]in[]分别记录每个点的所有出边的下界之和以及所有入边的下界之和(可以通过一个数组来完成,因为我们只想知道这个点到底是流出去还是流进来)。

S为源点,T为汇点。

(2)附加网络

加入虚拟源点SS和虚拟汇点TT。加入边<T, S> ,容量为正无穷。

对于原图中的每个点i(包括源点S和汇点T),如果in[i]>out[i],则加边<SS,i>,容量为in[i]-out[i] ,如果in[i]<out[i],则加边<i,TT>,容量为out[i]-in[i]

(3)判断有无解

设所有边的下界的和为sum,如果SSTT的最大流不等于sum,就无解。

还有其他判断方法,比如说判断以SS为起点的边的容量是否全部为0

(4)去边

第(2)步中加的边。对于<T, S >,增加权值为负无穷的<T ,S >就可以了。对于SS,TT的边,另head[SS] = head[TT] =-1就好了。

(5)求最大流(最小流)

最大流:去边以后运行ST的最大流算法。

最小流:去边以后运行TS的最大流算法。

每条边的流值等于flow(u,v) + low(u,v)。这个有多种计算方法。网上比较流行的方法是先加题目中输入的边(每个加的是两条),我们用一个数组low[]记录第一条边的下界。最后输出的答案是low[i] + edge[2*i+1].c 。当然下标是从0开始的。edge[2*i+1].c 需要解释一下,当算法运行以后,edge[2*i].c 都为0了,不然就无解。edge[2*i+1].cedge[2*i]的反向弧的权值,刚开始是0,但是算法运行完毕就等于原本的edge[2*i+1].c了。

  

3、习题

zoj2314 与sgu194本质上是同一题

题解看别人的。http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html

代码自己的,用的SAP。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int N=210;
  7 const int M=100000, INF=0x3f3f3f3f;
  8 struct node
  9 {
 10     int to,next,w;
 11 }edge[M];
 12 int head[N],numh[N],h[N],cure[N],pre[N];
 13 int ans,tot;
 14 int od[N],ind[N],low[M];//出边下界之和、入边
 15 void SAP(int s, int e,int n)
 16 {
 17     int flow,u,tmp,neck,i;
 18     ans=0;
 19     for(i=1;i<=n;i++)
 20         cure[i]=head[i];
 21     numh[0]=n;
 22     u=s;
 23     while(h[s]<n)
 24     {
 25         if(u==e)
 26         {
 27             flow =INF;
 28             for(i=s;i!=e;i=edge[cure[i]].to)
 29             {
 30                 if(flow>edge[cure[i]].w)
 31                 {
 32                     neck=i;
 33                     flow =edge[cure[i]].w;
 34                 }
 35             }
 36             for(i=s;i!=e;i=edge[cure[i]].to)
 37             {
 38                 tmp=cure[i];
 39                 edge[tmp].w-=flow;
 40                 edge[tmp^1].w+=flow;
 41             }
 42             ans+=flow;
 43             u=neck;
 44         }
 45         for(i=cure[u];i!=-1;i=edge[i].next)
 46             if(edge[i].w && h[u]==h[edge[i].to]+1) break;
 47         if(i!=-1) {cure[u]=i;pre[edge[i].to]=u;u=edge[i].to;}
 48         else
 49         {
 50             if(0==--numh[h[u]]) break;
 51             cure[u]=head[u];
 52             for(tmp=n,i=head[u];i!=-1;i=edge[i].next)
 53                 if(edge[i].w) tmp=min(tmp, h[edge[i].to]);
 54             h[u]=tmp+1;
 55             ++numh[h[u]];
 56             if(u!=s) u=pre[u];
 57         }
 58     }
 59 }
 60 void init()
 61 {
 62     tot=0;
 63     memset(head,-1,sizeof(head));
 64     memset(pre,-1,sizeof(pre));
 65     memset(h,0,sizeof(h));
 66     memset(numh,0,sizeof(numh));
 67     memset(ind,0,sizeof(ind));
 68     memset(od,0,sizeof(od));
 69     memset(low,0,sizeof(low));
 70 }
 71 void addedge(int i,int j,int w)
 72 {
 73     edge[tot].to=j;edge[tot].w=w;edge[tot].next=head[i];head[i]=tot++;
 74     edge[tot].to=i;edge[tot].w=0;edge[tot].next=head[j];head[j]=tot++;
 75 }
 76 int main()
 77 {
 78     //freopen("test.txt","r",stdin);
 79     int cas,i,j,k,n,m,a,b,s,e;
 80     scanf("%d",&cas);
 81     while(cas--)
 82     {
 83         scanf("%d%d",&n,&m);
 84         init();
 85         for(k=0;k<m;k++)
 86         {
 87             scanf("%d%d%d%d",&i,&j,&a,&b);
 88             addedge(i,j,b-a);
 89             od[i]+=a;  ind[j]+=a;
 90             low[k]=a;
 91         }
 92         s=n+1;e=s+1;
 93         for(i=1;i<=n;i++)
 94         {
 95             if(ind[i]>od[i]) addedge(s,i,ind[i]-od[i]);
 96             if(ind[i]<od[i]) addedge(i,e,od[i]-ind[i]);
 97         }
 98         SAP(s,e,e);
 99         int flag=1;
100         for(k=head[s];k!=-1;k=edge[k].next)
101         {
102             if(edge[k].w>0)
103             {
104                 flag=0;
105                 break;
106             }
107         }
108         if(!flag) printf("NO\n");
109         else
110         {
111             printf("YES\n");
112             for(i=0;i<m;i++)
113             {
114                 printf("%d\n",low[i]+edge[2*i+1].w);
115             }
116         }
117         if(cas) printf("\n");
118     }
119     return 0;
120 }
View Code

 

zoj3229

题解看别人的。http://www.cnblogs.com/wuyiqi/archive/2012/03/15/2398559.html  代码风格跟我类似

我最开始用SAP算法,怎么也AC不了。zoj2314都可以的。我检查了几个小时,最后都崩溃了。最后实在没办法了,换了Dinic的模版,没有修改就AC了。无语了。他们的时间复杂度的级别是一样的,并且SAP用了Dinic的优化思想,为什么就没办法过呢?

AC代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int N=2000;
  7 const int M=1000000, INF=100000000;
  8 struct node
  9 {
 10     int to,next,w;
 11 }edge[M];
 12 int level[N],que[N],head[N];
 13 int tot,ans;
 14 bool makelevel(int s, int t)
 15 {
 16     memset(level,0,sizeof(level));
 17     level[s]=1;
 18     int iq=0 , i, k, top;
 19     que[iq++]=s;
 20     for(i=0;i<iq;i++)
 21     {
 22         top=que[i];
 23         if(top==t) return 1;
 24         for(k=head[top];k!=-1;k=edge[k].next)
 25             if(!level[edge[k].to] && edge[k].w)
 26             {
 27                 que[iq++]=edge[k].to;
 28                 level[edge[k].to]=level[top]+1;
 29             }
 30     }
 31     return 0;
 32 }
 33 int DFS(int now, int maxf, int t)
 34 {
 35     if(now ==t) return maxf;
 36     int ret=0, f, k;
 37     for(k=head[now];k!=-1;k=edge[k].next)
 38     {
 39         if(edge[k].w && level[edge[k].to]==level[now]+1)
 40         {
 41             f=DFS(edge[k].to, min(maxf-ret,edge[k].w), t);
 42             edge[k].w-=f;
 43             edge[k^1].w+=f;
 44             ret+=f;
 45             if(ret==maxf) return ret;
 46         }
 47     }
 48     return ret;
 49 }
 50 void DINIC(int s, int t)
 51 {
 52     ans=0;
 53     while(makelevel(s,t)) ans+=DFS(s,INF,t);
 54 }
 55 void addedge(int i,int j,int w)
 56 {
 57     edge[tot].to=j;edge[tot].w=w;edge[tot].next=head[i];head[i]=tot++;
 58     edge[tot].to=i;edge[tot].w=0;edge[tot].next=head[j];head[j]=tot++;
 59 }
 60 int p[N] ,ind[N],low[100010];
 61 int main()
 62 {
 63     //freopen("test.txt","r",stdin);
 64     int i,j,k,n,m,a,b,s,e,res,cnt,g;
 65     while(scanf("%d%d",&n,&m)!=EOF)
 66     {
 67         tot=0;
 68         memset(head,-1,sizeof(head));
 69         memset(ind,0,sizeof(ind));
 70         s=n+m+1;e=s+1;//源点,汇点
 71         for(i=1;i<=m;i++)
 72         {
 73             scanf("%d",&g);
 74             ind[i+n]-=g; ind[e]+=g;
 75         }
 76         cnt=0;
 77         for(i=1;i<=n;i++)//n天
 78         {
 79             scanf("%d%d",&k,&p[i]);//该天最多给r人拍照,最多拍p张
 80             while(k--)
 81             {
 82                 scanf("%d%d%d",&j,&a,&b);
 83                 j+=n+1;
 84                 addedge(i,j,b-a);
 85                 ind[i]-=a; ind[j]+=a;
 86                 low[cnt++]=a;//第cnt条边的下界
 87             }
 88         }
 89         for(i=1;i<=n;i++) addedge(s,i,p[i]);
 90         for(i=1;i<=m;i++) addedge(i+n,e,INF);
 91 
 92         //原图构造完毕
 93         addedge(e,s,INF);
 94         int st=e+1,ed=st+1;//超级源点、超级汇点
 95         for(i=1;i<=n+m+2;i++)
 96         {
 97             if(ind[i]>0) addedge(st,i,ind[i]);
 98             if(ind[i]<0) addedge(i,ed,-ind[i]);
 99         }
100         DINIC(st,ed);
101         int flag=1;
102         for(k=head[st];k!=-1;k=edge[k].next)
103         {
104             if(edge[k].w)
105             {
106                 flag=0;
107                 break;
108             }
109         }
110         if(!flag) printf("-1\n");
111         else
112         {
113             DINIC(s,e);
114             addedge(e,s,-INF);//删除添加的边
115             res=0;
116             for(i=head[s];i!=-1;i=edge[i].next)
117                 res+=edge[i^1].w;
118             printf("%d\n",res);
119             for(i=0;i<cnt;i++)
120             {
121                 printf("%d\n",low[i]+edge[2*i+1].w);
122             }
123         }
124         printf("\n");
125     }
126     return 0;
127 }
View Code

 

posted @ 2014-09-30 23:43  pengmq  阅读(629)  评论(0编辑  收藏  举报