摘自https://www.cnblogs.com/liu-runda/p/6262832.html
無源匯有上下界可行流(也就是循環流)
求出一個流:每條邊的流量必須>=Li且<=Hi || 每個點必須滿足總流入量=總流出量
算法的核心:將一個不滿足流量守恆的初始流調整成滿足流量守恆的流.(調整)
如果存在一個可行流,那么一定滿足每條邊的流量都大於等於流量的下限.
因此我們可以令每條邊的流量等於流量下限,得到一個初始流,然后建出這個流的殘量網絡.(即:每條邊的流量等於這條邊的流量上限與流量下限之差)
這個初始流不一定滿足流量守恆,因此最終的可行流一定是在這個初始流的基礎上增大了一些邊的流量使得所有點滿足流量守恆.
因此我們考慮在殘量網絡上求出另一個不滿足流量守恆的附加流,使得這個附加流和我們的初始流合並之后滿足流量守恆.即:
如果某個點在所有邊流量等於下界的初始流中滿足流量守恆,那么這個點在附加流中也滿足流量守恆,
如果某個點在初始流中的流入量比流出量多x,那么這個點在附加流中的流出量比流入量多x.
如果某個點在初始流中的流入量比流出量少x,那么這個點在附加流中的流出量比流入量少x.
可以認為附加流中一條從u到v的邊上的一個流量代表將原圖中u到v的流量增大1
基礎流:采用最低限額
當然會存在流的落差,對於落差,可以調整補流,最多可補(最高 - 最低),以最多可補建圖
所以我們求出的流量 + 原基礎流中的流量 就是平衡流的流量 ———— 可達平衡的條件就是我們求出的流量 = 源點流出的流量和(也就是落差全部滿足)
在基礎流,不平衡的落差流要添加源點匯點使之平衡
基礎流中流入多,流出少的點,我們要在該流中嘗試增加流出,以使之平衡,所以要從源點到該點引一條邊,流量為其差值,如果這條邊被滿足,就可以補差這個點為平衡點了
反之,基礎流中流入少,流出多的點,我們要在該流中嘗試增加流入,以使之平衡,所以要從該點到匯點引一條邊,如果這條邊被滿足,意義就是流入被增加,就可以補差這個點為平衡點了
最后如果都能被滿足,那么就存在平衡流,流量為基礎流中的邊流量 + 我們新建的流中的對應邊流量,否則不能滿足
對於不能滿足的情況,一般都是觸碰到了上界,因為我們新建的補充流中,邊流的容量,是原圖中最大可擴充流量
如果看不懂的化,建議模擬一下這個樣例
1 2 3 7
2 3 4 6
3 1 2 9
輸出應該是4 4 4的循環流
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define inf (1 << 28)
using namespace std;
const int maxn = 300,maxm=1e5+1e2;
//前向星
struct node{
int to,val,lid,pre;
}e[maxm];
int id[maxn],cnt = 0;
//dinic
int cur[maxn];
int flor[maxn];//BFS的分層處理
int n,m;//基礎變量
int upflow[maxn];//差流(可升流)
int retf[maxm];//結果
int low[maxm];//下界
void init()
{
memset(id,-1,sizeof(id));
memset(upflow,0,sizeof(upflow));
cnt = 0;
}
//網絡流加邊
void add(int from,int to,int val,int lid)
{
e[cnt].lid = lid;
e[cnt].to = to;
e[cnt].val = val;
e[cnt].pre = id[from];
id[from] = cnt++;
swap(from,to);
e[cnt].lid = lid;
e[cnt].to = to;
e[cnt].val = 0;
e[cnt].pre = id[from];
id[from] = cnt++;
}
//Dinic
//bfs分層
bool bfs(int s,int t)
{
memset(flor,0,sizeof(flor));
flor[s] = 1;
queue<int> q;
while(q.size())q.pop();
q.push(s);
while(q.size())
{
int now = q.front();
q.pop();
for(int i = id[now];~i;i = e[i].pre)
{
int to = e[i].to;
int val = e[i].val;
if(val > 0 && flor[to] == 0)
{
flor[to] = flor[now] + 1;
//printf("%d flor = %d\n",to,flor[to]);
q.push(to);
if(to == t)return 1;
}
}
}
return 0;
}
int dfs(int s,int t,int value)
{
//printf("s t value = ::: %d %d %d\n",s,t,value);
if(s == t || value == 0)return value;
int ret = value,a;
for(int &i = cur[s];~i;i = e[i].pre)
{
int to = e[i].to;
int val = e[i].val;
//printf("to = %d val = %d flornow = %d florto = %d\n",to,val,flor[s],flor[to]);
if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
{
//printf("a = %d\n",a);
e[i].val -= a;
e[i^1].val += a;
ret -= a;
if(ret == 0)break;
}
}
if(ret == value)flor[s] = 0;
return value - ret;
}
int dinic(int s,int t)
{
int ret = 0;
while(bfs(s,t))
{
memcpy(cur,id,sizeof(id));
ret += dfs(s,t,inf);
//cout<<ret<<endl;
}
return ret;
}
int main()
{
int T;
scanf("%d",&T);
int s,t;
while(T--)
{
init();
scanf("%d%d",&n,&m);
s = 0;
t = n + 1;
int from,to,up;
//附加流基礎建圖,表示基礎流最大可擴充的容量
for(int i = 1;i <= m;++i)
{
scanf("%d%d%d%d",&from,&to,&low[i],&up);
add(from,to,up - low[i],i);
upflow[from] -= low[i];
upflow[to] += low[i];
//printf("Line : %d - %d - %d\n",from,to,up-low[i]);
}
//補充流建邊
int sumf = 0;
for(int i = 1;i <= n;++i)
{
if(upflow[i] < 0)//流入 < 流出
{
add(i,t,-upflow[i],0);
//printf("Line : %d - %d - %d\n",i,t,-upflow[i]);
}
else//流入 > 流出
{
sumf += upflow[i];
add(s,i,upflow[i],0);
//printf("Line : %d - %d - %d\n",s,i,upflow[i]);
}
}
if(dinic(s,t) == sumf)
{
printf("YES\n");
for(int now = 1;now <= n;++now)
{
for(int i = id[now];~i;i = e[i].pre)
{
int lid = e[i].lid;
// i % 2 == 0對應的是正向邊
if(lid == 0 || i % 2 == 0)continue;
//printf("%d + %d === %d\n",low[lid],e[i].val,i);
retf[lid] = low[lid] + e[i].val;
}
}
for(int i=1;i<=m;++i)printf("%d\n",retf[i]);
}
else
{
printf("NO\n");
}
if(T)printf("\n");
}
return 0;
}
