//poj 1273 || hdu 1532 Drainage Ditches 最大流
//普通最大流
//这里给出dinic的递归和非递归代码
//非递归:
//用dfs或bfs寻找一条增广路,并进行分层,比如 i能到 j,
//则 j的层次比 i多 1。如果聚点有在 增广路上(即有对应的
//层次,否则说明已经达到最大流了),然后顺着层次寻找一条
//到达聚点的路,并记录下来,若某个结点流向下一结点时,流
//用不完,则说明可能有分支,记录最后一个分支,若找到聚点
//时停止查找。如果路径上最后一个结点为聚点时,计算能到达
//聚点的流量,并建立反向边。之后从分支点找另一条能到聚点
//的路。若路径上最后一个结点不是聚点时,跳向分支点前一个
//结点找路,若一直找不到,直到源点时,继续dfs或bfs找增广路
//递归:
//bfs对残余网络分层,若到达不了sink则表示没有增广路了,
//否则sink也分完层后就开始深搜从前一个节点到下
//一个节点要有容量且下一个节点的层次要比前一节点多1才可继续深搜
//非递归:记得保存分支的地方(上条边的流量流了一些到聚点后还有剩时);
//注意用dinic的话如果用邻接表会tle,用邻接矩阵则不会。
//dinic时,若深搜到的点不能到达sink的点的层次要标为 -1,
//表示不能到达sink
//具体看代码
递归
//递归 #define comein freopen("in.txt", "r", stdin); #include <stdio.h> #include <string.h> #include <queue> #include <algorithm> using namespace std; #define INF 1<<30 #define N 500 int eid, n_point; int map[N][N]; //用邻接表居然tle了 int head[N], to[N], cap[N], next[N]; int level[N]; void add_edge(int from, int t, int f) { to[eid] = t; cap[eid] = f; next[eid] = head[from]; head[from] = eid++; to[eid] = from; //建反向边 cap[eid] = 0; next[eid] = head[t]; head[t] = eid++; } bool bfs(int root) //残余网络分层 { memset(level, -1, sizeof(level)); queue<int>que; que.push(root); level[root] = 0; while(!que.empty()) { root = que.front(); que.pop(); // for(int i = head[root]; i != -1; i = next[i]) for(int i = 1; i <= n_point; ++i) { //边还有残余容量,且下一个点还没分层 if(map[root][i] > 0 && level[i] == -1) { level[i] = level[root] + 1; if(i == n_point) return true; que.push(i); } // if(level[to[i]] == -1 && cap[i]) // { // level[to[i]] = level[root] + 1; // if(to[i] == n_point) // return true; // que.push(to[i]); // } } } return false; } int dfs(int root, int f) { if(root == n_point) return f; int flow, ans = 0; // for(int i = head[root]; i != -1; i = next[i]) for(int i = 1; i <= n_point; ++i) { // int m = min(cap[i], f - ans); //记得这里要 f-ans int m = min(map[root][i], f-ans); if(map[root][i] > 0 && level[root] + 1 == level[i] && ans < f && (flow = dfs(i, m) ) ) { map[root][i] -= flow; map[i][root] += flow; ans += flow; } // if(cap[i] && level[root]+1 == level[to[i]] && ans < f && // (flow = dfs(to[i], m ) ) ) // { //下标从0开始,则正向边为偶数,反向边为基数; // cap[i] -= flow; // cap[i^1] += flow;//下标从1开始则不能用 异或 // ans += flow; // } } if(ans == 0) //若没找到增广路,这时注意要把该点 level[root] = -1; //的层次标为-1,表示不能到达 return ans; } void dinic() { int flow = 0; while(bfs(1)) //分层 { while(1) //每条增广路都有一个瓶颈(增光路上最小容量的边) { //每次分层都要把所有增广路的瓶颈都清除掉,再重新分层 int f = dfs(1, INF);//因此最多层分v次 if(f == 0) break; flow += f; } } printf("%d\n", flow); } int main() { comein eid = 0; int n_edge; while(scanf("%d%d", &n_edge, &n_point) != EOF) { memset(map, 0, sizeof(map)); // memset(head, -1, sizeof(head)); while(n_edge--) { int from, to, f; scanf("%d%d%d", &from, &to, &f); map[from][to] += f; // add_edge(from, to, f); } dinic(); } return 0; }
非递归
//Dinic 非递归 //邻接矩阵 #include <stdio.h> #include <string.h> #define N 205 int n_node, n_edge; int map[N][N], level[N], que[N*N], path[N]; int bfs(int s) { memset(level, -1, sizeof(level)); int head = 0, tail = 0; que[tail++] = s; level[s] = 1; //源点标记为第一层 while(head < tail) { int now = que[head++]; for(int i = 1; i <= n_node; ++i) { if(map[now][i] != 0 && level[i] == -1) { level[i] = level[now] + 1; //标记层数 que[tail++] = i; } } } //如果聚点没有被分层的话,说明已经达到最大流了 return level[n_node] + 1; } int dinic(int s) { int max_flow = 0; while(bfs(s)) //广搜对一条增广路上的点分层 { int pos = 0, cut; path[++pos] = s; //记录增广路 while(1) { int flow = 1 << 30; int find = 1; //若增广路的最后一个结点是聚点,或者流不能流到下一个结点时,结束 while(path[pos] != n_node && find) { find = 0; for(int i = 1; i <= n_node; ++i) { if(level[i] == level[path[pos]] + 1 && map[path[pos]][i] != 0) { find = 1; path[++pos] = i; break; } } } if(path[pos] == n_node) //若有找到增广路 { for(int i = 1; i < pos; ++i) { if(flow > map[path[i]][path[i+1]]) { flow = map[path[i]][path[i+1]]; cut = i; //记录分叉的地方,这个地方的流没全用完 } } max_flow += flow; for(int i = 1; i < pos; ++i) { map[path[i]][path[i+1]] -= flow; map[path[i+1]][path[i]] += flow; //建反向边 } pos = cut; //从流没用完的边,也就是分支处继续找下一条到聚点的路 } else //若没有到达聚点 { if(pos == 1) break; else { level[path[pos]] = -1; //若这个点不能到达聚点,则去掉 pos--; } } } } return max_flow; } int main() { while(scanf("%d%d", &n_edge, &n_node) != EOF) { memset(map, 0, sizeof(map)); for(int i = 0; i < n_edge; ++i) { int from, to, flow; scanf("%d%d%d", &from, &to, &flow); map[from][to] += flow; } printf("%d\n", dinic(1)); } return 0; }
EK算法
//EK算法比较容易理解 //用广搜找到一条增广路,记录路径,找出增广路中最小 //的边为流到汇点的流量,建反向边,一直重复道没增广路为止; #include <iostream> #include <stdio.h> #include <string.h> #include <queue> using namespace std; #define INF (1<<30) #define N 205 int map[N][N], fa[N]; bool vis[N]; bool bfs(int n) //广搜找增广路 { memset(vis, false, sizeof(vis)); memset(fa, -1, sizeof(fa)); queue<int>que; que.push(1); vis[1] = true; while(!que.empty()) { int now = que.front(); que.pop(); vis[now] = true; for(int i = 1; i <= n; ++i) { if(vis[i] == false && map[now][i] > 0) { vis[i] = true; fa[i] = now; //记录路径 if(i == n) return true; que.push(i); } } } return false; } void EK(int n, int m) //找增广路 { int ans = 0; while(bfs(n)) { int min = INF; //找出增广路的瓶颈,即增广路中的最短边 for(int i = n; i != 1; i = fa[i]) min = map[fa[i]][i] < min ? map[fa[i]][i] : min; for(int i = n; i != 1; i = fa[i]) { map[fa[i]][i] -= min; map[i][fa[i]] += min; //增加反向流 } ans += min; } printf("%d\n", ans); } int main() { int n, m; while(scanf("%d%d", &m, &n) != EOF) { memset(map, 0, sizeof(map)); for(int i = 0; i < m; ++i) { int from, to, f; scanf("%d%d%d", &from, &to, &f); map[from][to] += f; } EK(n, m); } return 0; }