可行流
摘自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的循环流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | #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; } |
__EOF__

本文链接:https://www.cnblogs.com/DF-yimeng/p/9721556.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律