【圖論】無源匯上下界可行/最大流
無源匯上下界可行/最大流
可行流即對於一張無源點與匯點的流量網路\(G\)
詢問是否存在一種流量方案,使得每條邊的流量在對應的上下界內,且每個點流量平衡
最大流則是在可行流的基礎上求出最大流量
解法
既然對於流量網路\(G\)內每一條邊的流量都進行了限制,使其必須處於\([L_i,R_i]\)之內
那麼就假設每條邊一開始就存在著要求的最小流量\(L_i\)
這條邊此時的容量便變成了\(R_i-L_i\),初始流量為\(0\),以此初步建立流量網路
這樣處理便解決了上下界的限制
但同時也導致每個點的流量不一定平衡,即流入量≠流出量
所以接下來要對點進行平衡處理,使得每條邊與原圖中對應的邊相加後,全圖流量平衡
構建虛擬源點\(S\)與虛擬匯點\(T\),使得能夠虛擬提供流量
由於上面我們處理時使得每條邊都固定擁有了流量\(L_i\)
那麼對於這條邊連線的兩個點\(u_i,v_i\)
即\(u_i\)的流出量\(out[u_i]+=L_i\),\(v_i\)的流入量\(in[v_i]+=L_i\)
處理完所有邊後,得到每個點的總流入量與總流出量
可得,如果對於點\(i\)
\(in[i]>=out[i]\):該點流入量較流出量大,說明點\(i\)需要多向外輸出\(in[i]-out[i]\)的流量,那麼這些流量就從虛擬源點\(S\)取,故建立\(S\)至\(i\)的流量為\(in[i]-out[i]\)
\(in[i]<out[i]\):該點流出量較流入量大,說明外面需要多向點\(i\)輸入\(out[i]-in[i]\)的流量,換言之可以將該點多餘的流量流向虛擬匯點\(T\),故建立\(i\)至\(T\)的流量為\(out[i]-in[i]\)的一條邊
可行流求解
如上建立流量網路
可知,對於與源點/匯點連線的邊的容量和代表著全圖的流量總和
令所有\(in[i]>=out[i]\)情況下的\(in[i]-out[i]\)總和為\(sum\)(或所有\(in[i]<out[i]\)情況下的\(out[i]-in[i]\)總和)
對於這個流量網路,只有當最大流量流滿\(sum\)
所以直接跑Dinic,判斷最大流是否等於\(sum\)即可
最大流求解
如上,採用Dinic演算法跑出來的圖即代表著最大流
例題
模板題,多組資料,給定點數與邊數,給出每條邊連向即上下界,問一種使得流量平衡的可行解
最後取出跑最大流後每條邊對應的反向邊流量作為該邊的流量,加上初始的流量下界作為該邊最終流量即可
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=200,maxm=40000;
struct edge{
int u,v,cap,flow;
edge(){}
edge(int u,int v,int cap,int flow):u(u),v(v),cap(cap),flow(flow){}
}eg[maxm<<1];
int tot,s,t,dis[maxn<<1],cur[maxn<<1];
vector<int> tab[maxn<<1];
void init(int n,int s_,int t_){
while(n)tab[n--].clear();
tot=0,s=s_,t=t_;
}
void addedge(int u,int v,int cap){
tab[u].push_back(tot);
eg[tot++]=edge(u,v,cap,0);
tab[v].push_back(tot);
eg[tot++]=edge(v,u,0,0);
}
int bfs(){
queue<int> q;
q.push(s);
memset(dis,INF,sizeof dis);
dis[s]=0;
while(!q.empty()){
int h=q.front(),i;
q.pop();
for(i=0;i<tab[h].size();i++){
edge &e=eg[tab[h][i]];
if(e.cap>e.flow&&dis[e.v]==INF){
dis[e.v]=dis[h]+1;
q.push(e.v);
}
}
}
return dis[t]<INF;
}
int dfs(int x,int maxflow){
if(x==t|maxflow==0)
return maxflow;
int flow=0,i,f;
for(i=cur[x];i<tab[x].size();i++){
cur[x]=i;
edge &e=eg[tab[x][i]];
if(dis[e.v]==dis[x]+1&&(f=dfs(e.v,min(maxflow,e.cap-e.flow)))>0){
e.flow+=f;
eg[tab[x][i]^1].flow-=f;
flow+=f;
maxflow-=f;
if(maxflow==0)
break;
}
}
return flow;
}
int dinic(){
int flow=0;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
struct node
{
int u,v,l,r;
}E[maxm<<1];
int in[maxn],out[maxn];
void solve()
{
int n,m,u,v,sum=0;
scanf("%d%d",&n,&m);
init(n+2,n+1,n+2);
for(int i=1;i<=n;i++)
in[i]=out[i]=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&E[i].u,&E[i].v,&E[i].l,&E[i].r);
in[E[i].v]+=E[i].l;
out[E[i].u]+=E[i].l;
addedge(E[i].u,E[i].v,E[i].r-E[i].l); //限制流量為r-l
}
for(int i=1;i<=n;i++)
{
if(in[i]>=out[i])
addedge(s,i,in[i]-out[i]),sum+=in[i]-out[i];
else
addedge(i,t,out[i]-in[i]);
}
if(dinic()<sum)
{
puts("NO");
return;
}
puts("YES");
for(int i=1;i<2*m;i+=2)
printf("%d\n",E[i/2+1].l+eg[i^1].flow); //從跑出的殘量網路中取反向邊的flow為當前最大流量,加上原先的下界作為流量
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}