摘自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 ",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 ",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 ",to,val,flor[s],flor[to]); if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val)))) { //printf("a = %d ",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 ",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 ",i,t,-upflow[i]); } else//流入 > 流出 { sumf += upflow[i]; add(s,i,upflow[i],0); //printf("Line : %d - %d - %d ",s,i,upflow[i]); } } if(dinic(s,t) == sumf) { printf("YES "); 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 ",low[lid],e[i].val,i); retf[lid] = low[lid] + e[i].val; } } for(int i=1;i<=m;++i)printf("%d ",retf[i]); } else { printf("NO "); } if(T)printf(" "); } return 0; }