【网络流】[USACO4.2]草地排水Drainage Ditches
用EdmondsKarp可过
题目背景
在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水。这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间。因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没的烦恼(不用担心,雨水会流向附近的一条小溪)。作为一名一流的技师,农夫约翰已经在每条排水沟的一端安上了控制器,这样他可以控制流入排水沟的水流量。
题目描述
农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。需要注意的是,有些时候从一处到另一处不只有一条排水沟。
根据这些信息,计算从水潭排水到小溪的最大流量。对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。
输入输出格式
输入格式:
第1行: 两个用空格分开的整数N (0 <= N <= 200) 和 M (2 <= M <= 200)。N是农夫John已经挖好的排水沟的数量,M是排水沟交叉点的数量。交点1是水潭,交点M是小溪。
第二行到第N+1行: 每行有三个整数,Si, Ei, 和 Ci。Si 和 Ei (1 <= Si, Ei <= M) 指明排水沟两端的交点,雨水从Si 流向Ei。Ci (0 <= Ci <= 10,000,000)是这条排水沟的最大容量。
输出格式:
输出一个整数,即排水的最大流量。
题目分析
嗯这就是一道EdmondsKarp的模板题。(时间复杂度:O(NM²))
对于EdmondsKarp这个算法,重点在于它的增广操作。因为每一次增广是对于路径上的单独一条流而言的,所以需要搜索每一条s→t的流,并且回溯增广这条流上的流量,因此实现方式应该选择BFS而非DFS(DFS太容易被卡挂了)。
这个算法还有一个巧妙的地方,就是它不仅更新s→t的流量使其加上delta,同时更新t→s的流量使其减去delta(残量网络建图)。这样可以解决一些本应该需要指数级复杂度才能解决的麻烦情况。
一边冷静分析一边抄的刘汝佳的代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e6+35; 4 const int INF = 2e9; 5 struct Edge 6 { 7 int from, to, cap, flow; 8 Edge(int u, int v, int c, int f):from(u),to(v),cap(c),flow(f) {} 9 }; 10 struct EdmondsKarp 11 { 12 int n,m; 13 vector<Edge> edges; 14 vector<int> G[maxn]; //邻接表(但我个人不喜欢这种方式) 15 int a[maxn],p[maxn]; 16 17 void init(int n) //初始化 18 { 19 for (int i=0; i<=n; i++)G[i].clear(); 20 edges.clear(); 21 } 22 23 void add_edge(int from, int to, int cap) //邻接表加边 24 { 25 edges.push_back(Edge(from, to, cap, 0)); 26 edges.push_back(Edge(to, from, 0, 0)); 27 int mx = edges.size(); 28 G[from].push_back(mx-2); 29 G[to].push_back(mx-1); //一对相反弧存在一起。所以编号分别为x,x^1(异或) 30 } 31 32 long long Maxflow(int s, int t) //s→t的最大流 33 { 34 long long flow = 0; 35 for (;;) 36 { 37 memset(a, 0, sizeof(a)); 38 queue<int> Q; 39 Q.push(s); 40 a[s] = INF; 41 while (Q.size()) //BFS搜索 42 { 43 int x = Q.front();Q.pop(); 44 for (int i=0; i<G[x].size(); i++) 45 { 46 Edge& e = edges[G[x][i]]; 47 if (!a[e.to] && e.cap>e.flow) 48 { 49 p[e.to] = G[x][i]; 50 a[e.to] = min(a[x], e.cap-e.flow); 51 Q.push(e.to); 52 } 53 } 54 if (a[t])break; //如果终点可增广,则退出BFS 55 } 56 if (!a[t])break; //如果搜索完之后终点仍然不可增广,则退出最大流过程 57 for (int u=t; u!=s; u = edges[p[u]].from) //回溯增广 58 { 59 edges[p[u]].flow += a[t]; 60 edges[p[u]^1].flow -= a[t]; 61 } 62 flow += a[t]; 63 } 64 return flow; 65 } 66 }f; 67 int read() //对快读不熟悉的后果!最早是没写return,后来发现ch应该要getchar()读入。于是读入这里使得程序从 9272ms→ 632ms! 68 { //还以为EK写错了…… 69 char ch=getchar();int num = 0; 70 while (ch<'0'||ch>'9')ch = getchar(); 71 while (ch>='0'&&ch<='9'){num=num*10+ch-'0';ch = getchar();} 72 return num; 73 } 74 int main() 75 { 76 scanf("%d%d",&f.m,&f.n); 77 f.init(f.n); 78 for (int i=1; i<=f.m; i++) 79 { 80 int x = read(), y = read(), z = read(); 81 f.add_edge(x, y, z); 82 } 83 printf("%lld\n",f.Maxflow(1, f.n)); 84 }
另附:
1.Cptraser:www.cnblogs.com/Cptraser/p/7921455.html
2.网络:blog.sina.com.cn/s/blog_6cf509db0100uy5n.html
听说NOIp提高组不考网络流的样子(?)下午继续去做usacoTraing