看到网上好多都用的链式前向星,就我在用 \(vector\) 邻接表……算法
先来介绍一些相关的定义。(我的理解)数组
一个网络是一张带权的有向图 \(G=(V,E)\) ,其中每任意一条边 \((u,v)\) 的权值称为这条边的容量 \(c(u,v)\) 。若这条边不存在,对应的容量就为 \(0\) 。其中包含两个特殊的点:源点 \(S\) 与汇点 \(T\) 。网络
\(f\) 为网络的流函数,每一条边都有对应的流量。对于合法的流函数包含如下性质。函数
对于一个网络,不难发现合法的流函数不少。这张图的流量为 \(∑_{k∈E}f(S,k)\) ,顾名思义,最大流就是这张网络的最大流量。ui
存在一条从 \(S\) 到 \(T\) 的路径,使得路径上全部的流量都不为 \(0\) ,则称该路径为增广路。spa
对于任意时刻,当前时刻网络中,由全部结点与剩余容量大于 \(0\) 的边构成的该网络的子图被称为残量网络。code
在这个算法中,知足层数 \(de[v]=de[u]+1\) 的边 \((u,v)\) ,所构成的子图称为分层图。get
一条边中须要包含如下信息:终点节点编号,边的容量,相反的边的编号。string
struct Node { int to, value, rev; Node() {} Node(int T, int V, LL R) { to = T;//节点编号 value = V;//边的容量 rev = R;//相反的边的编号 } };
得双向存边,给出一条边 \((A,B)\) ,其长度为 \(C\) ,建一条从 \(A\) 到 \(B\) 的边,权值为 \(C\) ,与之相反的边权值为 \(0\)。io
for(int i = 1; i <= m; i++) { Quick_Read(A); Quick_Read(B); Quick_Read(C); int idA = v[A].size(), idB = v[B].size(); v[A].push_back(Node(B, C, idB)); v[B].push_back(Node(A, 0, idA)); }
双向存边是为了给后面 \(dfs\) 时,若存在更优解,可使得程序反悔,从新走另外一条路。这里暂时不懂能够继续看后面的代码再来理解这样建图的意义。
重复上述两个操做,直到当前网络中不存在增广路。
先来看 \(bfs\) 。其返回值为 \(bool\) ,意为该残量网络中是否还存在增广路。层数 \(de[i]\) 的意义很明白: \(S\) 到达当前的点 \(i\) 的最小步数。而按照这样的分层,每次只能将当前流量信息传递到下一层数节点上,能够很大程度上避免张冠李戴的状况。若 \(T\) 的层数为 \(1\) ,则说明当前 \(S\) 不能通向 \(T\) ,故而不存在增广路,跳出循环。
bool bfs_Dinic() {//bfs将残余网络分层,返回是否图中还存有增广路 memset(de, 0, sizeof(de));//清空深度 while(!q.empty()) q.pop(); q.push(s); de[s] = 1; be[s] = 0; while(!q.empty()) { int now = q.front(); q.pop(); int SIZ = v[now].size(); for(int i = 0; i < SIZ; i++) { int next = v[now][i].to; if(v[now][i].value && !de[next]) { q.push(next); be[next] = 0;//分层图改变,必须改变be[next]的值 de[next] = de[now] + 1; if(next == t) return true; } } } return false; }
再来看 \(dfs\) ,来判断每一次的网络是否能够传递,完成增广的过程(如下代码附上注释)。这样一次走了不止 \(1\) 条增广路,节省了很多时间。
int dfs_Dinic(int now, int flow) { if(now == t)//找到汇点 return flow; int i, surp = flow;//当前剩余流量 int SIZ = v[now].size(); for(i = be[now]; i < SIZ && surp; i++) { int next = v[now][i].to, valedge = v[now][i].value; if(valedge && de[next] == de[now] + 1) {//&&前判断是否能够走,便是剩余流量是否为0;&&后判断是否知足当前残余网络分层要求 int maxnow = dfs_Dinic(next, Min(surp, valedge)); if(!maxnow)//经验定增广完毕,de这个点不须要在遍历了 de[next] = 0; v[now][i].value -= maxnow; v[next][v[now][i].rev].value += maxnow;//反悔,可能找到其余路径比当前这个流大 surp -= maxnow; } } be[now] = i;//i以前的增广路已经更新完 return flow - surp; }
最后在来讲说剪枝, \(be\) 数组,在遍历 \(i\) 时,\(be[i]\) 以前的路径已经找完增广路了,而对于当前这个分层图,不存在会有更优解的状况,程序也不须要反悔,并不会影响程序的正确性,因此直接就不须要遍历以前的点。
单看这段程序,能够发现时间复杂度为 \(O(n^2m)\) 。
int Dinic() { int res = 0, flow = 0; while(bfs_Dinic()) while(flow = dfs_Dinic(s, INF))//最大流的定义 res += flow;//流量守恒 return res; }
而其实实际上并不须要这么多时间,参考资料得知能够处理\(10^4\)~\(10^5\)这样规模的网络。
#include <queue> #include <cstdio> #include <vector> #include <cstring> using namespace std; #define LL long long #define INF 0x3f3f3f3f #define Min(a, b) ((a) < (b) ? (a) : (b)) void Quick_Read(LL &N) { N = 0; LL op = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') op = -1; c = getchar(); } while(c >= '0' && c <= '9') { N = (N << 1) + (N << 3) + (c ^ 48); c = getchar(); } N *= op; } const LL MAXN = 2e2 + 5; struct Node { LL to, value, rev; Node() {} Node(LL T, LL V, LL R) { to = T; value = V; rev = R; } }; vector<Node> v[MAXN]; queue<LL> q; LL de[MAXN], be[MAXN]; LL n, m, s, t; bool bfs_Dinic() { memset(de, 0, sizeof(de)); while(!q.empty()) q.pop(); q.push(s); de[s] = 1; be[s] = 0; while(!q.empty()) { LL now = q.front(); q.pop(); LL SIZ = v[now].size(); for(int i = 0; i < SIZ; i++) { LL next = v[now][i].to; if(v[now][i].value && !de[next]) { q.push(next); be[next] = 0; de[next] = de[now] + 1; if(next == t) return true; } } } return false; } LL dfs_Dinic(LL now, LL flow) { if(now == t) return flow; int i, surp = flow; LL SIZ = v[now].size(); for(i = be[now]; i < SIZ && surp; i++) { LL next = v[now][i].to, valedge = v[now][i].value; if(valedge && de[next] == de[now] + 1) { LL maxnow = dfs_Dinic(next, Min(surp, valedge)); if(!maxnow) de[next] = 0; v[now][i].value -= maxnow; v[next][v[now][i].rev].value += maxnow; surp -= maxnow; } } be[now] = i; return flow - surp; } LL Dinic() { LL res = 0, flow = 0; while(bfs_Dinic()) while(flow = dfs_Dinic(s, INF)) res += flow; return res; } void Read() { LL A, B, C; Quick_Read(n); Quick_Read(m); Quick_Read(s); Quick_Read(t); for(int i = 1; i <= m; i++) { Quick_Read(A); Quick_Read(B); Quick_Read(C); LL idA = v[A].size(), idB = v[B].size(); v[A].push_back(Node(B, C, idB)); v[B].push_back(Node(A, 0, idA)); } } int main() { Read(); printf("%lld", Dinic()); return 0; }