最大流算法总结
来源:互联网 发布:物理机安装centos系统 编辑:程序博客网 时间:2024/05/20 10:10
Edmonds-Karp:
http://blog.csdn.net/moon_1st/article/details/5305524
这个网站上讲解了一些概念,可以了解一下。
Edmonds-Karp算法是Ford-Fulkerson方法的bfs实现,每次找增广路,把这条路上的所有点的流量加上这条路上的残余容量,再找新的增广路,直到找不到为止。
在无权边的有向图中寻找最短路,最简单的方法就是广度优先搜索 (BFS),E-K 算法就直接来源于此。每次用一遍 BFS 寻找从源点 s 到终点 t 的最短路作为增广路径,然后增广流量 f 并修改残量网络,直到不存在新的增广路径。
E-K 算法的时间复杂度为 O(VE2),由于 BFS 要搜索全部小于最短距离的分支路径之后才能找到终点,因此可以想象频繁的 BFS 效率是比较低的。
下面时代码,其中fa存储的是增光路径,f是记录流量的相邻矩阵,c是记录容量的矩阵,需要注意的是queue队列不要开成全局变量:
void Edmonds_Karp(){ int i,d; long long ans=0; int s; fa[m]=1; while(fa[m]!=0) { queue<int> zui; zui.push(1); memset(fa,0,sizeof(fa)); fa[1]=1; while(!zui.empty()&&fa[m]==0) { i=zui.front(); zui.pop(); for(int j=1; j<=m; j++) { if(fa[j]==0) if(f[i][j]<c[i][j]) { fa[j]=i; zui.push(j); } else if(f[j][i]>0) { fa[j]=-i; zui.push(j); } } } if(fa[m]!=0) { i=m; d=INT_MAX; while(i!=1) { if(fa[i]>0) { if(c[fa[i]][i]-f[fa[i]][i]<d) d=c[fa[i]][i]-f[fa[i]][i]; } else if(f[i][-fa[i]]<d) d=f[i][-fa[i]]; i=abs(fa[i]); } ans+=d; i=m; while(i!=1) { if(fa[i]>0) f[fa[i]][i]+=d; else f[i][-fa[i]]-=d; i=abs(fa[i]); } } }}
#include<cstdio> //EK()算法。时间复杂度(VE^2)#include<queue>#include<cstring>using namespace std;const int maxn = 100;const int INF = (1<<30)-1;int g[maxn][maxn];int flow[maxn],pre[maxn];bool vis[maxn];int n,m;int bfs(int s,int e){ memset(pre,-1,sizeof(pre)); memset(vis,false,sizeof(vis)); queue<int > q; vis[s] = true; for(int i=1;i<=n;i++) flow[i]=INF; q.push(s); while(!q.empty()){ int now = q.front(); q.pop(); if(now==n) break; for(int i=1;i<=n;i++){ //寻找增广路最小流量 if(!vis[i]&&g[now][i]>0){ vis[i] = true; flow[i] = min(flow[now],g[now][i]); pre[i] = now; q.push(i); } } } if(!vis[e]|| e==1) //找不到完整的增广路or源点汇点重合 return -1; else return flow[e];}int EK(int s,int e){ int temp,d,res,maxflow; maxflow = 0; while((d=bfs(s,e))!=-1){ maxflow += d; temp=n; while(temp!=1){ res = pre[temp]; g[res][temp]-=d; //正向边 g[temp][res]+=d; //反向边 temp = res; } } return maxflow;}int main(){ int T,ca=1; int start,end,capacity; scanf("%d",&T); while(T--){ memset(g,0,sizeof(g)); scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&start,&end,&capacity); g[start][end]+=capacity; } printf("Case %d: %d\n",ca++,EK(1,n)); } return 0;}
const int INF=999999999;int c[110][110],f[110][110],pre[110];int q[110],d[110];int n,m,s,t,res;bool bfs(){ int i,j,tail=1,head=0; q[tail]=s; pre[s]=-1; memset(d,0,sizeof(d)); d[s]=INF; while(head<tail) { i=q[++head]; for(j=1;j<=n*2;j++) { if(c[i][j]>f[i][j]&&!d[j]) { d[j]=min(d[i],c[i][j]-f[i][j]); pre[j]=i; q[++tail]=j; if(j==t) return true; } } } return false;}int EK(){ res=0; while(bfs()) { for(int i=t;i!=s;i=pre[i]) { f[pre[i]][i]+=d[t]; f[i][pre[i]]-=d[t]; } res+=d[t]; } return res;}
Dinic算法(借鉴的别人的):
算法步骤:
Dinic算法的思想也是分阶段地在层次图中增广。它与最短路径增值算法不同之处是:在Dinic算法中,我们用一个dfs过程代替多次bfs来寻找阻塞流。下面给出其算法步骤:
- 初始化流量,计算出剩余图
- 根据剩余图计算层次图。若汇点不在层次图内,则算法结束
- 在层次图内用一次dfs过程增广
- 转步骤2
- 用BFS建立分层图
注意:分层图是以当前图为基础建立的,所以要重复建立分层图 - 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数
- 重复步骤2,直到DFS找不到新的路径时,重复步骤1
- 初始化流量,计算出剩余图
- 根据剩余图计算层次图。若汇点不在层次图内,则算法结束
- 在层次图内用一次dfs过程增广
- 转步骤2
注意(可以无视):
- Dinic(其实其他的好多)算法中寻找到增广路后要将反向边增加I
- Dinic中DFS时只在分层图中DFS,意思是说DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1,例如在图1中第一个次DFS中,1->2->4 这条路径是不合法的,因为Dis[2]=1;Dis[4]=1;
下面是dfs的过程:
ps;While outdegree(s)>0 up.top; if u<>t if outdegree(u)>0 设(u,v)为层次图中的一条边; pp,v; else 从p和层次图中删除点u, 以及和u连接的所有边; else 增广p(删除了p中的饱和边); 令p.top为p中从s可到达的最后顶点;end while
在程序里,p表示找到的增广路径,p.top为路径中的最后一个顶点。一开始,p中只有源点。
整个While循环分为2个操作。如果p的最后一个顶点为汇点,也就是说找到了增广路,那么对p增广,注意到增广后一定有一条或多条p中的边被删除了。这时,我们使增广路径后退至p中从源点可到达的最后一个顶点。
如果p的最后一个顶点不为汇点,那么观察最后那个的顶点u 。若在层次图中存在从u连出的一条边,比如(u,v),我们就将顶点v放入路径p中,继续dfs遍历;否则,点u对之后的dfs遍历就没有用了,我们将点u以及层次图中连到u的所有边删除,并且在p中后退一个点。
Dfs过程将会不断重复这2个操作,直到从源点连出的边全部被删除为止。
二、Dinic 算法模板
void Dinic(){ for(;;){ BFS(); if(D[T]==-1)break; int path_n=0; int x=S; memcpy(cur,E,sizeof(cur)); for(;;){ if(x==T){ int mink=-1,delta=INT_MAX; for(int i=0;i<path_n;++i){ if(path[i]->c<delta){ delta=path[i]->c; mink=i; } } for(int i=0;i<path_n;++i){ path[i]->c-=delta; path[i]->back->c+=delta; } path_n=mink; x=path[path_n]->x; } edge* e; for(e=cur[x];e;e=e->next){ if(e->c==0) continue; int y=e->y; if(D[x]+1==D[y]) break; } cur[x]=e; if(e){ path[path_n++]=e; x=e->y; } else{ if(path_n==0) break; D[x]=-1; --path_n; x=path[path_n]->x; } } }}
#include<iostream>#define Max 210int flow[Max][Max],d[Max]; //flow is the networkint sta,end,N; //sta is the sourse ,end is the,N is the number of vectorbool bfs(int s){ int front=0,rear=0; int q[Max]; memset(d,-1,sizeof(d)); //d[] is the deep q[rear++]=s; d[s]=0; while(front<rear) { int k=q[front++]; for(int i=0; i<=N; i++) if(flow[k][i]>0&&d[i]==-1) { d[i]=d[k]+1; q[rear++]=i; } } if(d[end]>=0) return true; return false;}int dinic(int k,int sum) //k is the sourse{ if(k==end) return sum; int os=sum; for(int i=0; i<=N&∑ i++) if(d[i]==d[k]+1&&flow[k][i]>0) { int a=dinic(i,min(sum,flow[k][i])); //Deep to the end. flow[k][i]-=a; flow[i][k]+=a; sum-=a; } return os-sum;}int main(){ int ret=0; while(bfs(sta)) ret+=dinic(sta,INT_MAX); printf("%d\n",ret); return 0;}
还有一个更高效的算法,据说是没有数据能卡住,SAP。
SAP算法模板:
点击打开链接
http://blog.csdn.net/lhshaoren/article/details/7795342
typedef struct node{ int v, w; struct node *nxt, *op; }NODE; NODE edg[MM]; // 保存所有的边 NODE *link[NN]; // 记录节点所在链表的首节点 int h[NN]; // 距离标号,记录每个点到汇点的距离,这里的距离指的是层数 int num[NN]; // gap优化,标号为i的顶点个数 int M, N, idx, S, T, n; // S 表示源点,T表示汇点,n表示节点个数 void add(int u, int v, int c){//很典型的加边法,不过是用链表存储的。 idx++; edg[idx].v = v; edg[idx].w = c; edg[idx].nxt = link[u]; edg[idx].op = edg + idx + 1; link[u] = edg + idx; idx++; edg[idx].v = u; edg[idx].w = 0; edg[idx].nxt = link[v]; edg[idx].op = edg + idx - 1; link[v] = edg + idx; } int Min(int a, int b){ return a < b ? a : b; } int aug(int u, int flow){ if (u == T) return flow; int l = flow; // l表示剩余容量 int tmp = n - 1; for (NODE *p = link[u]; p; p = p->nxt){//遍历u点相关联的其它点 if (h[u] == h[p->v] + 1 && p->w){ int f = aug(p->v, Min(l, p->w)); l -= f; p->w -= f; p->op->w += f; if (l == 0 || h[S] == n) return flow - l; // gap } // 这里是有剩余容量的可行边 if (p->w > 0 && h[p->v] < tmp){ tmp = h[p->v]; } } if(l == flow){// 如果没有找到增流,才修改标号,刚开始写错了,也杯具的过了好多题 num[h[u]]--; // gap if (num[h[u]] == 0) h[S] = n; // gap,每个点的距离值最多为n - 1,这里设为n 表示断层了 else{ h[u] = tmp + 1; num[h[u]]++; // gap } } return flow - l;.//这里是L不是1. } /*n表示总点的个数,包括源点和汇点*/ void sap(){ int ans = 0; memset(h, 0, sizeof(h)); // h 保存的是距离标号(到汇点的) memset(num, 0, sizeof(num)); num[0] = n; while(h[S] < n){ ans += aug(S, INF); } printf("%d\n", ans); }
邻接表实现:
const int MAXN=20010;//点数的最大值const int MAXM=880010;//边数的最大值const int INF=0x3f3f3f3f;struct Node{ int from,to,next; int cap;}edge[MAXM];int tol;int head[MAXN];int dep[MAXN];int gap[MAXN];//gap[x]=y :说明残留网络中dep[i]==x的个数为yint n;//n是总的点的个数,包括源点和汇点void init(){ tol=0; memset(head,-1,sizeof(head));}void addedge(int u,int v,int w){ edge[tol].from=u; edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u]; head[u]=tol++; edge[tol].from=v; edge[tol].to=u; edge[tol].cap=0; edge[tol].next=head[v]; head[v]=tol++;}void BFS(int start,int end){ memset(dep,-1,sizeof(dep)); memset(gap,0,sizeof(gap)); gap[0]=1; int que[MAXN]; int front,rear; front=rear=0; dep[end]=0; que[rear++]=end; while(front!=rear) { int u=que[front++]; if(front==MAXN)front=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(dep[v]!=-1)continue; que[rear++]=v; if(rear==MAXN)rear=0; dep[v]=dep[u]+1; ++gap[dep[v]]; } }}int SAP(int start,int end){ int res=0; BFS(start,end); int cur[MAXN]; int S[MAXN]; int top=0; memcpy(cur,head,sizeof(head)); int u=start; int i; while(dep[start]<n) { if(u==end) { int temp=INF; int inser; for(i=0;i<top;i++) if(temp>edge[S[i]].cap) { temp=edge[S[i]].cap; inser=i; } for(i=0;i<top;i++) { edge[S[i]].cap-=temp; edge[S[i]^1].cap+=temp; } res+=temp; top=inser; u=edge[S[top]].from; } if(u!=end&&gap[dep[u]-1]==0)//出现断层,无增广路 break; for(i=cur[u];i!=-1;i=edge[i].next) if(edge[i].cap!=0&&dep[u]==dep[edge[i].to]+1) break; if(i!=-1) { cur[u]=i; S[top++]=i; u=edge[i].to; } else { int min=n; for(i=head[u];i!=-1;i=edge[i].next) { if(edge[i].cap==0)continue; if(min>dep[edge[i].to]) { min=dep[edge[i].to]; cur[u]=i; } } --gap[dep[u]]; dep[u]=min+1; ++gap[dep[u]]; if(u!=start)u=edge[S[--top]].from; } } return res;}
- 最大流算法总结
- 最大流/最小割算法总结
- ACM图算法——最大流各种算法总结
- 网络流/最大流算法与题目总结
- 网络流/最大流算法与题目总结
- [总结] 网络流最大流算法反向边的作用
- 网络流学习:最大流Dinic算法总结
- POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结
- 最大连续区间和的算法总结
- 【算法】最大似然估计总结笔记
- 最大流算法 -- Dinic算法
- 【算法导论】最大流算法
- 最大流算法模板
- 最大流SAP算法
- 最大流算法
- Dinic算法最大流。。
- 最大流算法模板
- 最大流算法实现
- Linux设备驱动程序——Linux设备驱动程序——内存和I/O基础知识学习:(2)内核中相关基础知识学习
- SSI(Struts2, Spring, iBatis)框架整合图示
- Android之多媒体--使用VideoView播放视频
- ARM分类
- Linux线程-互斥锁pthread_mutex_t
- 最大流算法总结
- C++对象语义与值语义
- 【Code Library】不超过25个页面的材料
- word下不能使用搜狗输入法
- Android之多媒体--使用MediaPlayer和SurfaceView播放视频
- StringUtils. indexOf 用法
- JSP向Servlet传中文
- tomcat修改启动内存
- 背包问题解析