网络流入门总结(EK算法)
来源:互联网 发布:条件选股软件 编辑:程序博客网 时间:2024/06/05 03:04
终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着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 210using 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\n",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 1010using 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\n",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 440using 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\n",p[sr[i]],p[ta[i]]); //printf("%d %d\n",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\n",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 250using 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\n",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 220using 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 guanstruct 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\n",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 440using 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\n");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\n",EK()); } return 0;}
0 0
- 网络流入门总结(EK算法)
- hdu1532 Drainage Ditches (网络流入门)&(EK算法模板)
- 网络流入门 最大流,带下界,最小费用,EK算法,Dinic算法 模板
- POJ 1459 PowerNetwork 多源点网络流入门(EK算法求最大流)
- HDU1532Drainage Ditches(网络流入门+EK模板题)
- hdu 3549 Flow Problem 最大流入门 EK算法
- poj 1273 EK最大流入门题
- hdu 1532 Drainage Ditches 和hdu 3549 Flow Problem 网络流入门(EK和dinic)
- 图论、网络流入门题目总结
- 网络流EK算法
- 网络流EK算法
- 网络流EK算法
- 网络流EK算法
- 网络流EK算法
- 网络流--EK算法
- 网络流入门--最大流算法Dicnic 算法
- 网络流入门
- 网络流入门
- Redis 主从复制
- 航天+互联网
- linux内核内存分配(一、基本概念)
- linux线程介绍
- Merge Sorted Array
- 网络流入门总结(EK算法)
- POJ 1745:Divisibility 枚举某一状态的DP
- 3个gif动画让你明白RotateAnimation的pivotX与pivotY
- Hello World♂
- 开发前奏—熟悉业务
- 阿里推荐大赛:ODPS SQL 构建离线评估
- 线程私有数据TSD——一键多值技术,线程同步中的互斥锁和条件变量
- openssl dgst命令使用示例
- C语言实现链队列代码