学习网络流中的最大流,自然要先学会基础的最大流算法。这里参考学习挑战写了两个测试代码,学习了通过寻找增广路计算最大流。
1 /* Ford_Fulkerson算法 2 *进行f(最大流)次dfs(), 3 * O(f|E|) 4 */ 5 #include <bits/stdc++.h> 6 using namespace std; 7 const int INF = 0x3f3f3f3f; 8 const int max_V = 1010; 9 struct Edge{ //终点,容量, 反向边 10 int to, cap, rev; 11 }; 12 vector<Edge> G[max_V]; //邻接表建图 13 bool used[max_V]; //dfs()用到的访问标记 14 int n, k; 15 void add_edge(int from, int to, int cap){ 16 G[from].push_back(Edge{to, cap, G[to].size()}); 17 G[to].push_back(Edge{from, 0, G[from].size() - 1}); 18 } 19 int dfs(int v, int t, int f){ 20 if(v == t) return f; 21 used[v] = true; 22 for(int i = 0; i<G[v].size(); i++){ 23 Edge &e = G[v][i]; 24 if(!used[e.to] && e.cap>0){ 25 int d = dfs(e.to, t, min(f, e.cap)); 26 if(d > 0){ 27 e.cap -= d; 28 G[e.to][e.rev].cap += d; 29 return d; 30 } 31 } 32 } 33 return 0; 34 } 35 int cnt; 36 int max_flow(int s, int t){ 37 int flow = 0; 38 while(true){ 39 memset(used, 0, sizeof(used)); 40 int f = dfs(s, t, INF); 41 42 printf("残余网络上的增广路--------- %d ", ++cnt); 43 for(int i=0; i<=n; i++){ 44 for(int j=0; j<G[i].size(); j++){ 45 printf("[%d %d %d %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?" ":" "); 46 } 47 } 48 49 if(f == 0){ 50 return flow; 51 } 52 flow += f; 53 } 54 } 55 int main(){ 56 freopen("in.txt", "r", stdin); 57 58 for(int i=0; i<n; i++){ 59 G[i].clear(); 60 } 61 scanf("%d%d", &n, &k); 62 for(int i=0; i<k; i++){ 63 int fr, to, ca; 64 scanf("%d%d%d",&fr, &to, &ca); 65 add_edge(fr, to, ca); 66 } 67 for(int i=0; i<=n; i++){ 68 for(int j=0; j<G[i].size(); j++){ 69 printf("[%d %d %d %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?" ":" "); 70 } 71 } 72 cout << endl; 73 printf("%d ", max_flow(1, n)); 74 return 0; 75 } 76
1 /* 2 * Dinic算法 3 * O(|E||V^2|) 4 */ 5 #include <bits/stdc++.h> 6 using namespace std; 7 const int INF = 0x3f3f3f3f; 8 const int max_V = 1010; 9 struct Edge{ //终点,容量, 反向边 10 int to, cap, rev; 11 }; 12 vector<Edge> G[max_V]; 13 int level[max_V]; //顶点到原点的距离标号 14 int iter[max_V]; //当前弧,在其之前的边无用 15 int n, k; 16 void add_edge(int from, int to, int cap){ 17 G[from].push_back( (Edge){to, cap, G[to].size()} ); 18 G[to].push_back((Edge){from, 0, G[from].size()-1}); //G[from].size()-1: 为to->from的反向边编号,即边(from->to)的编号 19 } 20 /* 21 * 通过bfs()计算从源点出发的距离标号 22 */ 23 void bfs(int s){ 24 memset(level, -1, sizeof(level)); 25 queue<int> que; 26 level[s] = 0; 27 que.push(s); 28 while(!que.empty()){ 29 int v = que.front(); que.pop(); 30 for(int i=0; i<G[v].size(); i++){ 31 Edge &e = G[v][i]; 32 if(e.cap > 0 && level[e.to] < 0){ 33 level[e.to] = level[v] + 1; 34 que.push(e.to); 35 } 36 } 37 } 38 } 39 /* 40 * dfs()寻找增广路 41 */ 42 int dfs(int v, int t, int f){ 43 if(v == t) return f; 44 for(int &i = iter[v]; i < G[v].size(); i++){ 45 Edge &e = G[v][i]; 46 if(e.cap > 0 && level[v] < level[e.to]){ 47 //level[e.to] == level[v] - 1 48 int d = dfs(e.to, t, min(f, e.cap)); 49 if(d > 0){ 50 e.cap -= d; 51 G[e.to][e.rev].cap += d; 52 return d; 53 } 54 } 55 } 56 return 0; 57 } 58 /* 59 * 求s到t的最大流 60 */ 61 int cnt; /// 62 int max_flow(int s, int t){ 63 int flow = 0; 64 while(true){ 65 bfs(s); 66 if(level[t] < 0) return flow; 67 memset(iter, 0, sizeof(iter)); 68 int f; 69 while((f = dfs(s, t, INF)) > 0){ 70 flow += f; 71 } 72 printf("残余网络上的增广路--------- %d ", ++cnt); 73 for(int i=0; i<=n; i++){ 74 for(int j=0; j<G[i].size(); j++){ 75 printf("[%d %d %d %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?" ":" "); 76 } 77 } 78 79 } 80 } 81 int main(){ 82 freopen("in.txt", "r", stdin); 83 84 for(int i=0; i<n; i++){ 85 G[i].clear(); 86 } 87 scanf("%d%d", &n, &k); 88 for(int i=0; i<k; i++){ 89 int fr, to, ca; 90 scanf("%d%d%d",&fr, &to, &ca); 91 add_edge(fr, to, ca); 92 } 93 for(int i=0; i<=n; i++){ 94 for(int j=0; j<G[i].size(); j++){ 95 printf("[%d %d %d %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?" ":" "); 96 } 97 } 98 cout << endl; 99 printf("%d ", max_flow(1, n)); 100 return 0; 101 }
这篇文章对一些概念铺垫的很详细:网络流(一) 入门到熟练
A - A Plug for UNIX
题意:
n种插座各一个接在电源上,
m个用电器,每个用电器有一个插座类型,
k种转换器,可以将一种类型转换为另一种。每种转换器可以用无数个。
问最少有多少用电器不能用。
Thinking:
- 注意转换器第一个数字是插座类型,提供给用电器,第二个数字是插头类型,提供给插座。
- 这里重点是把题目抽象为网络流的最大流问题,题说最少多少电器不能用,则思考最多多少电器能用。难点是如何建图。
- 建图:源点到每个插座连一条容量为1的边, 每个插座向每个类型相同的用电器连一条容量为1的边, 每个用电器向汇点连一条容量为1的边,转换器依题目定义连一条INF的边。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <map> 5 #include <vector> 6 #include <queue> 7 #include <string> 8 using namespace std; 9 const int INF = 0x3f3f3f3f; 10 const int maxn = 820; 11 struct edge{ 12 int to, cap, rev; 13 }; 14 vector<edge> G[maxn]; 15 int level[maxn]; 16 int iter[maxn]; 17 void add_edge(int from, int to, int cap){ 18 G[from].push_back((edge){to, cap, G[to].size()}); 19 G[to].push_back((edge){from, 0, G[from].size()-1}); 20 } 21 //submit时修改下这里,poj不支持这种写法 22 void bfs(int s){ 23 memset(level, -1, sizeof(level)); 24 queue<int> que; 25 level[s] = 0; 26 que.push(s); 27 while(!que.empty()){ 28 int v = que.front(); que.pop(); 29 for(int i=0; i<G[v].size(); i++){ 30 edge &e = G[v][i]; 31 if(e.cap > 0 && level[e.to] < 0){ 32 level[e.to] = level[v] + 1; 33 que.push(e.to); 34 } 35 } 36 } 37 } 38 int dfs(int v, int t, int f){ 39 if(v == t) return f; 40 for(int &i = iter[v]; i<G[v].size(); i++){ 41 edge &e = G[v][i]; 42 if(e.cap > 0 && level[e.to]>level[v]){ 43 int d = dfs(e.to, t, min(f, e.cap)); 44 if(d > 0){ 45 e.cap -= d; 46 G[e.to][e.rev].cap += d; 47 return d; 48 } 49 } 50 } 51 return 0; 52 } 53 int max_flow(int s, int t){ 54 int flow = 0; 55 while(true){ 56 bfs(s); 57 if(level[t] < 0) return flow; 58 memset(iter, 0, sizeof(iter)); 59 int f; 60 while((f = dfs(s, t, INF)) > 0) flow += f; 61 } 62 } 63 char str[30], stmp[30]; 64 int cnt; 65 map<string, int> rec; 66 int n, m, k; 67 void init(){ 68 for(int i=0; i<n; i++){ 69 G[i].clear(); 70 } 71 rec.clear(); 72 cnt = 3; 73 } 74 int need[maxn]; 75 int main(){ 76 freopen("in.txt", "r", stdin); 77 while(scanf("%d", &n) != EOF){ 78 init(); 79 int s = 1, t = 2; 80 ///// 81 /////建图是重点 82 ///// 83 /* 84 * 源点指向插座,容量为1 85 */ 86 for(int i=0; i<n; i++){ 87 str[0] = '