终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着LRJ的白书,看了好久,感觉可以了,然并卵,比赛的时候就签了一下到,就走了。。。从那以后就没碰过网络流,原来等着跟大工大神学习的,好像因为各种事情一直没有上这一课。然后又把白皮看了一遍,看了很多博客,感觉其中有一个人的博客写的特别好,现在找不到了。。。
跟着专题训练写了几道模板题,照着大白书稍微改了一点,成为了自己的模板。下面做一些入门网络流的总结:
1:反向边。每一条路径都对应着一条反向边,u->v流过f的流量,那个对应的反向边v->u流过-f的流量。而对应的反向边的容量cap = 0。
2:残量网络。就是把每一条路径剩余容量标称着条边的权值。(当然反向边也是必须要算的,这个时候要注意边的数量是原来边数量的两倍,权值>0才能形成一条边)
3:增广路。如果在残余网络中有一条路径能够从s(源点)走到t(汇点)那么,我们就称这条路为增广路。
4:增广路求最大流。增广路对应的每一条路径权值的最小值(d)全部加到原网络时,这个时候我们发现总的流量增加了d,(注意在d添加到原边时,对应的反向边的流量需要-d,因为是对应的关系,很好理解)。那么问题就转化成如何找到所有的增广路。这个就很显然了,我们只要遍历一遍残量网络图,就可以找到了。(BFS实现,DFS比BFS慢一点,有一个人的博客有测试的)
下面就说一个这6道模板题:
POJ_1273 http://poj.org/problem?id=1273 很裸的最大流题,正好可以检测模板的正确性:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> #define INF (1<<30) #define ll long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define N 210 using namespace std; struct Edge{ int from,to,cap,flow; Edge(){}; Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){} }edges[N<<1]; int edge_cnt; vector <int> G[N]; //存图 int pre[N],a[N]; //记录增广路路径 int s,t,n,m; bool vis[N]; void AddEdge(int from,int to,int cap){ edges[edge_cnt++] = Edge(from,to,cap,0); edges[edge_cnt++] = Edge(to,from,0,0); G[from].push_back(edge_cnt - 2); G[to].push_back(edge_cnt -1); } int bfs(){ memset(vis,false,sizeof(vis)); queue <int> q; q.push(s); a[s] = INF; while(!q.empty()){ int u = q.front(); q.pop(); if(u == t) break; FOR(i,0,G[u].size()){ int e = G[u][i]; int v = edges[e].to; if(!vis[v] && edges[e].cap > edges[e].flow){ vis[v] = true; pre[v] = e; a[v] = min(a[u],edges[e].cap-edges[e].flow); q.push(v); } } } if(!vis[t]) return -1; return a[t]; } int EK(){ int max_flow = 0; int tem; while((tem = bfs()) != -1){ max_flow += tem; for(int u = t; u != s; u = edges[pre[u]].from){ edges[pre[u]].flow += tem; edges[pre[u]^1].flow -= tem; } } return max_flow; } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d",&n,&m)){ FOR(i,0,N) G[i].clear(); s = 1; t = m; int u,v,c; edge_cnt = 0; FOR(i,0,n){ scanf("%d%d%d",&u,&v,&c); AddEdge(u,v,c); } printf("%d ",EK()); } return 0; }
HDU_3549 http://acm.hdu.edu.cn/showproblem.php?pid=3549 也是一个模板练手题
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> #define INF (1<<30) #define ll long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define N 1010 using namespace std; struct Edge{ int from,to,cap,flow; Edge(){}; Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){} }edges[N<<1]; int edge_cnt; vector <int> G[N]; //存图 int pre[N],a[N]; //记录增广路路径 int s,t,n,m; bool vis[N]; void AddEdge(int from,int to,int cap){ edges[edge_cnt++] = Edge(from,to,cap,0); edges[edge_cnt++] = Edge(to,from,0,0); G[from].push_back(edge_cnt - 2); G[to].push_back(edge_cnt -1); } int bfs(){ memset(vis,false,sizeof(vis)); queue <int> q; q.push(s); a[s] = INF; while(!q.empty()){ int u = q.front(); q.pop(); if(u == t) break; FOR(i,0,G[u].size()){ int e = G[u][i]; int v = edges[e].to; if(!vis[v] && edges[e].cap > edges[e].flow){ vis[v] = true; pre[v] = e; a[v] = min(a[u],edges[e].cap-edges[e].flow); q.push(v); } } } if(!vis[t]) return -1; return a[t]; } int EK(){ int max_flow = 0; int tem; while((tem = bfs()) != -1){ max_flow += tem; for(int u = t; u != s; u = edges[pre[u]].from){ edges[pre[u]].flow += tem; edges[pre[u]^1].flow -= tem; } } return max_flow; } int main() { //freopen("test.in","r",stdin); int T,tCase = 0; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); FOR(i,0,N) G[i].clear(); s = 1; t = n; int u,v,c; edge_cnt = 0; FOR(i,0,m){ scanf("%d%d%d",&u,&v,&c); AddEdge(u,v,c); } printf("Case %d: ",++tCase); printf("%d ",EK()); } return 0; }
POJ_1087 http://poj.org/problem?id=1087 这个题注意一下对应关系就好了,题目是插头可以变成别的种类的,不是插座可以变成别的种类的,我用的map来对应的节点,建图的时候麻烦一点。(其实也可以用二分图的匹配来写,本来二分图就可以用网络流来写的,所以不影响)在插头一边加源点,插座一边加汇点,转换器对应的路径,cap=1。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> #include <string> #include <map> #define INF (1<<30) #define ll long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define N 440 using namespace std; struct Str{ char s[30]; Str() {} Str(char* ths) {FOR(i,0,30) s[i] = ths[i];} bool operator < (const Str& rhs) const{ return strcmp(s,rhs.s) == -1 ? true : false; } }str[N]; int n,m,k; char src[N][30],tar[N][30],sr[N][30],ta[N][30]; int cnt,co; int l[N],r[N]; bool mat[N][N]; bool vis[N]; vector <int> GP[N]; map <string,int> p; void dfs(int u){ FOR(i,0,GP[u].size()){ int v = GP[u][i]; if(!vis[v]) {vis[v] = true;dfs(v);} } } ///ÍøÂçÁ÷Ïà¹Ø int s,t,edge_cnt; int a[N]; struct Edge{ int from,to,cap,flow; Edge() {} Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){} }edge[N*N*2]; vector <int> G[N]; int pre[N]; void AddEdge(int from,int to,int cap){ edge[edge_cnt++] = Edge(from,to,cap,0); edge[edge_cnt++] = Edge(to,from,0,0); G[from].push_back(edge_cnt-2); G[to].push_back(edge_cnt-1); } int bfs(){ memset(vis,false,sizeof(vis)); queue <int> q; a[s] = INF; q.push(s); vis[s] = true; while(!q.empty()){ int u = q.front(); q.pop(); if(u == t) break; FOR(i,0,G[u].size()){ int e = G[u][i]; int v = edge[e].to; if(!vis[v] && edge[e].cap > edge[e].flow){ a[v] = min(a[u],edge[e].cap-edge[e].flow); pre[v] = e; vis[v] = true; q.push(v); } } } if(!vis[t]) return -1; return a[t]; } int EK(){ int max_flow = 0; int d; while((d = bfs()) != -1){ max_flow += d; for(int u = t;u != s;u = edge[pre[u]].from){ edge[pre[u]].flow += d; edge[pre[u]^1].flow -= d; } } return max_flow; } /****/ void init(){ sort(str,str+co); cnt = 0; p[str[0].s] = (++cnt); FOR(i,1,co){ if(strcmp(str[i].s,str[i-1].s) != 0) p[str[i].s] = (++cnt); } FOR(i,0,N) GP[i].clear(); FOR(i,0,k){ GP[p[sr[i]]].push_back(p[ta[i]]); //printf("%d %d ",p[sr[i]],p[ta[i]]); //printf("%d %d ",p[sr[i]],GP[p[sr[i]]].size()); } memset(mat,false,sizeof(mat)); FOR(i,1,cnt+1){ memset(vis,false,sizeof(vis)); vis[i] = true; dfs(i); FOR(j,1,cnt+1){ if(vis[j]) mat[i][j] = true; } } FOR(i,n+1,n+m+1){ l[i] = p[tar[i]]; } FOR(i,1,n+1){ r[i] = p[src[i]]; } s = 0; t = n+m+1; FOR(i,0,N) G[i].clear(); edge_cnt = 0; FOR(i,n+1,n+m+1){ AddEdge(s,i,1); } FOR(i,1,n+1){ AddEdge(i,t,1); } FOR(i,n+1,n+m+1){ FOR(j,1,n+1){ if(mat[l[i]][r[j]]){AddEdge(i,j,1);} } } } int main() { //freopen("test.in","r",stdin); while(~scanf("%d",&n)){ co = 0; FOR(i,1,n+1){ scanf("%s",src[i]); str[co++] = Str(src[i]); } scanf("%d",&m); char tem[30]; FOR(i,n+1,n+m+1){ scanf("%s%s",tem,tar[i]); str[co++] = Str(tar[i]); } scanf("%d",&k); FOR(i,0,k){ scanf("%s%s",sr[i],ta[i]); str[co++] = Str(sr[i]); str[co++] = Str(ta[i]); } init(); printf("%d ",m-EK()); } return 0; }POJ_1274 http://poj.org/problem?id=1274 本题是一个裸的二分图最大匹配,正好练一下二分图最大匹配的模板。网络流当然也可以写,一边加源点,一边加汇点,容量都设为1,就变成了一个最大流问题。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <algorithm> #include <vector> #define ll long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define N 250 using namespace std; vector <int> G[N<<1]; int n,m; int march[N<<1]; bool vis[N<<1]; bool dfs(int u){ vis[u] = true; FOR(i,0,G[u].size()){ int v = G[u][i]; if(!vis[v]){ vis[v] = true; if(march[v] == -1 || dfs(march[v])){ march[v] = u; march[u] = v; return true; } } } return false; } int MaxMarch(){ int ans = 0; memset(march,-1,sizeof(march)); FOR(i,1,n+1){ if(march[i] == -1){ memset(vis,false,sizeof(vis)); if(dfs(i)) ans++; } } return ans; } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d",&n,&m)){ FOR(i,0,(n+m+1)) G[i].clear(); int cnt; int v; FOR(i,1,n+1){ scanf("%d",&cnt); while(cnt--){ scanf("%d",&v); G[i].push_back(v+n); G[v+n].push_back(i); } } printf("%d ",MaxMarch()); } return 0; }
POJ_1459 http://poj.org/problem?id=1459 题目乍一看有很多源点,汇点,其实都是假的,我们把所有源点全部连到一个s上,对应的路径cap就是这个点的cap,同理,所有的汇点都连到t上,cap对应的就是这个点的cap。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define ll long long #define INF (1<<30) #define N 220 using namespace std; inline void readint(int &ret) { char c; do { c = getchar(); } while(c < '0' || c > '9'); ret = c - '0'; while((c=getchar()) >= '0' && c <= '9') ret = ret * 10 + ( c - '0' ); } int n,np,nc,m; ///wangluoliu xiang guan struct Edge{ int from,to,cap,flow; Edge(){} Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){} }edge[N*N]; int s,t,edge_cnt,a[N],pre[N]; bool vis[N]; vector <int> G[N]; void AddEdge(int u,int v,int c){ G[u].push_back(edge_cnt); edge[edge_cnt++] = Edge(u,v,c,0); G[v].push_back(edge_cnt); edge[edge_cnt++] = Edge(v,u,0,0); } void init_ntf(){ FOR(i,0,N) G[i].clear(); edge_cnt = 0; } int bfs(){ memset(vis,false,sizeof(vis)); queue <int> q; q.push(s); a[s] = INF; vis[s] = true; while(!q.empty()){ int u = q.front(); q.pop(); if(u == t) break; FOR(i,0,G[u].size()){ int e = G[u][i]; int v = edge[e].to; if(!vis[v] && edge[e].cap > edge[e].flow){ a[v] = min(a[u],edge[e].cap - edge[e].flow); pre[v] = e; vis[v] = true; q.push(v); } } } if(!vis[t]) return -1; return a[t]; } int EK(){ int max_flow = 0; int d; while((d = bfs()) != -1){ max_flow += d; for(int u = t; u != s; u = edge[pre[u]].from){ edge[pre[u]].flow += d; edge[pre[u]^1].flow -= d; } } return max_flow; } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d%d%d",&n,&np,&nc,&m)){ s = 0; t = n+1; int u,v,l; init_ntf(); FOR(i,0,m){ readint(u); readint(v); readint(l); AddEdge(u+1,v+1,l); } FOR(i,0,np){ readint(v); readint(l); AddEdge(s,v+1,l); } FOR(i,0,nc){ readint(u); readint(l); AddEdge(u+1,t,l); } printf("%d ",EK()); } return 0; }
POJ_3281 http://poj.org/problem?id=3281 拆点+最大流。单纯地以为是二分图匹配肯定就贵了(反正我是没想出来二分图要怎么搞)。放到网络流里面,就会发现图建不出来!!!为什么???因为牛这个点可以经过无数遍,这个与题目要求显然不符合,这个时候要怎么办,就是把一头牛拆成牛1,牛2,对应的牛1,牛2之间连接一条容量为1的路径。这个样子就能保证每一头牛都最多走了一次。。。见图的关键就是每一条路径都与每一条路径实际一一对应!!!
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <vector> #include <queue> #define INF (1<<30) #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) #define ll long long #define N 440 using namespace std; struct Edge{ int from,to,cap,flow; Edge() {} Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){} }edge[N*N*2]; vector <int> G[N]; int edge_cnt,pre[N],a[N],f,d,n,s,t; bool vis[N]; void AddEdge(int u,int v,int c){ edge[edge_cnt++] = Edge(u,v,c,0); edge[edge_cnt++] = Edge(v,u,0,0); G[u].push_back(edge_cnt-2); G[v].push_back(edge_cnt-1); } int bfs(){ memset(vis,false,sizeof(vis)); queue <int> q; q.push(s); vis[s] = true; a[s] = INF; while(!q.empty()){ int u = q.front(); q.pop(); if(u == t) break; FOR(i,0,G[u].size()){ int e = G[u][i]; int v = edge[e].to; if(!vis[v] && edge[e].cap > edge[e].flow){ a[v] = min(a[u],edge[e].cap - edge[e].flow); pre[v] = e; vis[v] = true; q.push(v); } } } if(!vis[t]) return -1; return a[t]; } int EK(){ int max_flow = 0; int d; while((d = bfs()) != -1){ max_flow += d; for(int u = t; u != s; u = edge[pre[u]].from){ edge[pre[u]].flow += d; edge[pre[u]^1].flow -= d; } } return max_flow; } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d%d",&n,&f,&d)){ //if(n == 0 || f == 0 || d == 0) {printf("0 ");continue;} FOR(i,0,N) G[i].clear(); edge_cnt = 0; s = 0; t = f+n+n+d+1; FOR(i,1,n+1){ int cnt_f,cnt_d; scanf("%d%d",&cnt_f,&cnt_d); FOR(j,0,cnt_f){ int u; scanf("%d",&u); AddEdge(u,f+i,1); } AddEdge(f+i,f+i+n,1); FOR(j,0,cnt_d){ int v; scanf("%d",&v); AddEdge(f+i+n,f+n+n+v,1); } } FOR(i,1,f+1){ AddEdge(s,i,1); } FOR(i,f+n+n+1,t){ AddEdge(i,t,1); } printf("%d ",EK()); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。