有上下界的网络流
解决上下界网络流的一般思路:
解决这类问题的关键是如何去掉下界带来的麻烦。下面的哈工大出版的《图论及应用》里的思路。
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,如果SS到TT的最大流不等于sum,就无解。
还有其他判断方法,比如说判断以SS为起点的边的容量是否全部为0。
(4)去边
第(2)步中加的边。对于<T, S >,增加权值为负无穷的<T ,S >就可以了。对于SS,TT的边,另head[SS] = head[TT] =-1就好了。
(5)求最大流(最小流)
最大流:去边以后运行S到T的最大流算法。
最小流:去边以后运行T到S的最大流算法。
每条边的流值等于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].c是edge[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 }
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 }