先默認讀者有基礎的網絡流以及費用流的知識前置
1.有上下界無源點匯點的可行流問題:
在本文中指:
原圖中沒有任何一個點可以憑空產生流量,亦沒有任何一個點可以憑空消滅流量;
存在邊既有流量上界又有流量下界;
求每條邊流量的一組可行解;
滿足每個點的入流量等於出流量;
由題意可見本題的圖中有環,於是此類問題也被稱作循環流;
這里給出的解法是將本題轉換為一道普通的有上界最大流問題;
修改本題原圖中每條邊的流量下界為0,上界為原上界-原下界;
視為該邊現在已經擁有了等同於該邊流量下界的基礎流量了,
然而,由於每條邊在原圖中的流量下界不同,導致他們現在的基礎流量不同;
於是如果再讓現在圖中每個點出入相等,則表面相等,實則不同;
考慮當現在圖中一條邊i將x的流量匯入點a,實則匯入x+low[i]的流量(low[i]為i的下界),於是應當有額外一條low[i]的邊連入a
考慮當現在圖中一條邊i將x的流量運出點a,實則運出x+low[i]的流量(low[i]為i的下界),於是應當有額外一條low[i]的邊自a連出
由於這些額外的流量在現在圖中看來是憑空產生的,所以所有連入原圖的邊應當自一個額外的源點出發,設為S`
同理,所有連出原圖的邊應當去往一個額外的匯點,設為T`
然后,跑S`到T`的最大流,希望他可以使附加的邊滿流;
若附加邊滿流,則跑出了一組可行流,原圖中每條邊的可行流是他在現在圖中對應邊的流量加流量下界
若不滿流,則說明無論如何,原圖中總會有邊達不到下界,於是原圖不存在可行流;
注意:存在優化——即可以把所有同起點同終點的邊等效為一條流量為加和的邊,從S`到a的邊的流量可以1:1抵消掉從a到T`的流量
2.有上下界無源點匯點的最小費用流問題:
在本文中指:
在上個問題中的圖上加權的費用流問題;
這里給出的解法是直接在上題重構的圖中,給每條邊符合他來歷的權值即可;
由於一定附加邊滿流才有解,於是可以把附加邊貢獻的費用視為0上后單獨算出加到答案中去;
3.有上下界多個有限源匯點的可行流問題:
在本文中指:
在第一個問題的基礎上,有些點必須消滅一定的流量,有些點必須產生一定的流量;
對某些必須產生一定流量x的點,從S`連一條上限為x的附加邊;
對某些必須消滅一定流量x的點,向T`連一條上限為x的附加邊;
跑最大流,期望附加邊滿流;
4.問題3的最小費用流版本:
在問題3上附上費用,直接跑費用流即可
5.有上下界一組無限流量源匯點的可行流問題:
在本文中指:
存在邊既有流量上界又有流量下界;
存在一個源點S可以隨意產生流量,存在一個匯點T可以隨便消滅流量
求每條邊流量的一組可行解;
滿足源匯點之外的每個點入流量等於出流量;
可以看出S的流出等於T的流入;
於是建一條從T到S的流量無限的邊,本題的圖就屬於問題1了;
有趣的是,邊T->S的流量可以視作原圖中S到T的總流量(因為連上此邊后,S,T各自的出入相等)
6.問題5的最小費用流版本:
在問題5上附上費用,直接跑費用流即可
7.問題5的最大流版本:
先跑出問題5的一組可行流;
然而這組可行流並不一定是最大流;
發現一個性質,當我們采用增廣路算法求解最大流時,並不會修改源點到匯點路徑之外的邊的流量大小;
這意味着當我們第一次跑出問題5的一組可行解之后(跑這組可行解,即跑從S`到T`的最大流),
在現在的圖上直接跑S到T的最大流,
就能保證
1不會影響被視作原圖中邊流量下界的邊的流量情況——他們依然是滿流的;
2圖上連的T->S的INF邊的退流過程,等價於有一條S->T的邊在增廣,於是他自動把第一次可行流的答案加入第二次跑出的最大流中;
也就是說,這時跑出來的最大流可以被認為是滿足上下界前提下的最大流;
8.問題7的最小費用流版本:
在問題7上附上費用,直接跑費用流即可
題目:
bzojP2502
有上下界多個無限源點的費用流問題,好像保證了合法

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF=0x3f3f3f3f; 6 int n,S,T,ansf,answ; 7 struct ss{ 8 int next,to,f,v,cp; 9 }e[30010]; 10 int first[210],num; 11 int in[210]; 12 int que[100010],dis[210],vis[210],flow[210],pre[210],way[210]; 13 void bui_(int ,int ,int ,int ); 14 void build(int ,int ,int ,int ); 15 bool spfa(); 16 void EK(); 17 int main() 18 { 19 int i,j,k,l,o; 20 scanf("%d",&n); 21 S=n+1,T=S+1; 22 for(i=1;i<=n;i++) 23 bui_(S,i,INF,1); 24 for(i=1;i<=n;i++){ 25 scanf("%d",&k),o=0; 26 for(j=1;j<=k;j++){ 27 scanf("%d",&l); 28 bui_(i,l,INF,0); 29 in[l]++;o++; 30 } 31 bui_(i,T,o,0); 32 } 33 for(i=1;i<=n;i++) 34 bui_(S,i,in[i],0); 35 while(spfa()) 36 EK(); 37 printf("%d\n",ansf); 38 return 0; 39 } 40 void bui_(int f,int t,int fi,int vi){ 41 build(f,t,fi,vi),e[num].cp=num+1; 42 build(t,f,0,-vi),e[num].cp=num-1; 43 } 44 void build(int f,int t,int fi,int vi){ 45 e[++num].next=first[f]; 46 e[num].to=t,e[num].f=fi,e[num].v=vi; 47 first[f]=num; 48 } 49 bool spfa(){ 50 int i,h=0,t=1; 51 for(i=1;i<=T;i++)vis[i]=0,dis[i]=0x3f3f3f3f; 52 dis[S]=0,flow[S]=INF,que[t]=S; 53 while(h<t){ 54 vis[que[++h]]=0; 55 for(i=first[que[h]];i;i=e[i].next) 56 if(e[i].f&&dis[e[i].to]>dis[que[h]]+e[i].v){ 57 dis[e[i].to]=dis[que[h]]+e[i].v; 58 pre[e[i].to]=que[h],way[e[i].to]=i; 59 flow[e[i].to]=min(e[i].f,flow[que[h]]); 60 if(!vis[e[i].to]){ 61 que[++t]=e[i].to; 62 vis[que[t]]=1; 63 } 64 } 65 } 66 return dis[T]!=0x3f3f3f3f; 67 } 68 void EK(){ 69 int i; 70 ansf+=(flow[T]*dis[T]); 71 for(i=T;i;i=pre[i]) 72 if(way[i]) 73 e[way[i]].f-=flow[T],e[e[way[i]].cp].f+=flow[T]; 74 }
bzojP3876
同上,

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF=1000000000; 6 int n,S,T,ansf; 7 struct ss{ 8 int to,next,f,v,cp; 9 }e[26010]; 10 int first[1000],num; 11 int in[1010]; 12 int que[1000010],vis[1010],dis[1010],pre[1010],flow[1010],way[1010]; 13 void bui_(int ,int ,int ,int ); 14 void build(int ,int ,int ,int ); 15 bool spfa(); 16 void EK(); 17 int main() 18 { 19 int i,j,k,b,t,su; 20 scanf("%d",&n); 21 S=n+1,T=S+1; 22 bui_(S,1,INF,0); 23 for(i=1;i<=n;i++){ 24 scanf("%d",&k); 25 for(j=1;j<=k;j++){ 26 scanf("%d%d",&b,&t); 27 bui_(i,b,INF,t); 28 bui_(i,T,1,t); 29 in[b]++,su+=t; 30 } 31 } 32 for(i=1;i<=n;i++) 33 if(in[i]) 34 bui_(S,i,in[i],0); 35 bui_(S,1,INF,0); 36 while(spfa()) 37 EK(); 38 printf("%d\n",ansf); 39 } 40 void bui_(int f,int t,int fi,int vi){ 41 build(f,t,fi,vi),e[num].cp=num+1; 42 build(t,f,0,-vi),e[num].cp=num-1; 43 } 44 void build(int f,int t,int fi,int vi){ 45 e[++num].next=first[f]; 46 e[num].to=t,e[num].f=fi,e[num].v=vi; 47 first[f]=num; 48 } 49 bool spfa(){ 50 int i,h=0,t=1; 51 memset(vis,0,sizeof(vis)); 52 memset(dis,0x3f,sizeof(dis)); 53 dis[S]=0,flow[S]=INF,que[t]=S; 54 while(h<t){ 55 vis[que[++h]]=0; 56 for(i=first[que[h]];i;i=e[i].next) 57 if(e[i].f&&dis[e[i].to]>dis[que[h]]+e[i].v){ 58 dis[e[i].to]=dis[que[h]]+e[i].v; 59 pre[e[i].to]=que[h],way[e[i].to]=i; 60 flow[e[i].to]=min(e[i].f,flow[que[h]]); 61 if(!vis[e[i].to]){ 62 que[++t]=e[i].to; 63 vis[que[t]]=1; 64 } 65 } 66 } 67 return dis[T]!=0x3f3f3f3f; 68 } 69 void EK(){ 70 int i; 71 ansf+=(flow[T]*dis[T]); 72 for(i=T;i;i=pre[i]) 73 if(way[i]) 74 e[way[i]].f-=flow[T],e[e[way[i]].cp].f+=flow[T]; 75 }
bzojP2055
有上下界一個有上限源點的費用流問題,注意拆點限制流量

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF=0x3f3f3f3f; 6 int n,m,S,T,ansf,answ; 7 struct ss{ 8 int next,to,f,v,cp; 9 }e[30010]; 10 int first[210],num; 11 int que[100010],dis[210],vis[210],flow[210],pre[210],way[210]; 12 void bui_(int ,int ,int ,int ); 13 void build(int ,int ,int ,int ); 14 bool spfa(); 15 void EK(); 16 int main() 17 { 18 int i,j,k; 19 scanf("%d%d",&n,&m); 20 S=(n<<1)+2,T=S+1; 21 bui_(S,S-1,m,0); 22 for(i=1;i<=n;i++){ 23 scanf("%d",&j); 24 bui_(i,T,j,0),bui_(S,i+n,j,0); 25 bui_(S-1,i,INF,0); 26 } 27 for(i=1;i<n;i++) 28 for(j=1;j<=n-i;j++){ 29 scanf("%d",&k); 30 if(k!=-1) 31 bui_(i+n,i+j,INF,k); 32 } 33 while(spfa()) 34 EK(); 35 printf("%d\n",ansf); 36 return 0; 37 } 38 void bui_(int f,int t,int fi,int vi){ 39 build(f,t,fi,vi),e[num].cp=num+1; 40 build(t,f,0,-vi),e[num].cp=num-1; 41 } 42 void build(int f,int t,int fi,int vi){ 43 e[++num].next=first[f]; 44 e[num].to=t,e[num].f=fi,e[num].v=vi; 45 first[f]=num; 46 } 47 bool spfa(){ 48 int i,h=0,t=1; 49 for(i=1;i<=T;i++)vis[i]=0,dis[i]=0x3f3f3f3f; 50 dis[S]=0,flow[S]=INF,que[t]=S; 51 while(h<t){ 52 vis[que[++h]]=0; 53 for(i=first[que[h]];i;i=e[i].next) 54 if(e[i].f&&dis[e[i].to]>dis[que[h]]+e[i].v){ 55 dis[e[i].to]=dis[que[h]]+e[i].v; 56 pre[e[i].to]=que[h],way[e[i].to]=i; 57 flow[e[i].to]=min(e[i].f,flow[que[h]]); 58 if(!vis[e[i].to]){ 59 que[++t]=e[i].to; 60 vis[que[t]]=1; 61 } 62 } 63 } 64 return dis[T]!=0x3f3f3f3f; 65 } 66 void EK(){ 67 int i; 68 ansf+=(flow[T]*dis[T]); 69 for(i=T;i;i=pre[i]) 70 if(way[i]) 71 e[way[i]].f-=flow[T],e[e[way[i]].cp].f+=flow[T]; 72 }
bzojP3698
有上下界一組源匯點的最大流問題;

1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int INF=0x3f3f3f3f; 5 int n,S,T; 6 int in[210],out[210]; 7 struct ss{ 8 int next,to,f,cp; 9 }e[100010]; 10 int first[210],num; 11 int que[1000010],dep[210],cut[210]; 12 void bui_(int ,int ,int ); 13 void build(int ,int ,int ); 14 bool bfs(); 15 int dfs(int ,int ); 16 int main() 17 { 18 int i,j,k,add,ans=0; 19 double inpu; 20 scanf("%d",&n); 21 S=(n<<1)+1,T=S+1; 22 for(i=1;i<=n;i++) 23 for(j=1;j<=n;j++){ 24 scanf("%lf",&inpu); 25 if(i==n&&j==n)continue; 26 if(i==n){ 27 if(inpu>(int(inpu))) 28 bui_(j+n-1,T-2,1); 29 in[T-2]+=int(inpu),out[j+n-1]+=int(inpu); 30 continue; 31 } 32 if(j==n){ 33 if(inpu>(int(inpu))) 34 bui_(S-2,i,1); 35 in[i]+=int(inpu),out[S-2]+=int(inpu); 36 continue; 37 } 38 if(inpu>(int(inpu))) 39 bui_(i,j+n-1,1); 40 in[j+n-1]+=int(inpu),out[i]+=int(inpu); 41 } 42 bui_(T-2,S-2,INF); 43 for(i=1;i<=T;i++){ 44 if(in[i]>out[i])bui_(S,i,in[i]-out[i]); 45 if(out[i]>in[i])bui_(i,T,out[i]-in[i]),ans+=out[i]-in[i]; 46 } 47 while(bfs()) 48 while(add=dfs(S,INF)) 49 ans-=add; 50 if(ans){ 51 printf("No\n"); 52 return 0; 53 } 54 S-=2,T-=2; 55 while(bfs()) 56 while(add=dfs(S,INF)) 57 ans+=add; 58 printf("%d\n",ans*3); 59 return 0; 60 } 61 void bui_(int f,int t,int fi){ 62 build(f,t,fi),e[num].cp=num+1; 63 build(t,f,0),e[num].cp=num-1; 64 } 65 void build(int f,int t,int fi){ 66 e[++num].next=first[f]; 67 e[num].to=t,e[num].f=fi; 68 first[f]=num; 69 } 70 bool bfs(){ 71 int i,h=0,t=1; 72 memset(dep,0,sizeof(dep)); 73 for(i=1;i<=T;i++)cut[i]=first[i]; 74 que[t]=S,dep[S]=1; 75 while(h<t){ 76 h++; 77 for(i=first[que[h]];i;i=e[i].next) 78 if(e[i].f&&!dep[e[i].to]){ 79 que[++t]=e[i].to; 80 dep[que[t]]=dep[que[h]]+1; 81 } 82 } 83 return dep[T]; 84 } 85 int dfs(int now,int flow){ 86 int i,ret=0; 87 if(now==T) 88 return flow; 89 for(i=cut[now];i;i=e[i].next) 90 if(e[i].f&&dep[e[i].to]==dep[now]+1){ 91 ret=dfs(e[i].to,flow<e[i].f?flow:e[i].f); 92 if(ret){ 93 e[i].f-=ret; 94 e[e[i].cp].f+=ret; 95 cut[now]=i; 96 return ret; 97 } 98 } 99 cut[now]=0; 100 return 0; 101 }
吐槽一句:
bzoj什么也跑不過,luogu點數26w邊數150w都跑得過