(转自http://blog.csdn.net/wangjian8006/article/details/7939599)
EK(EdmondsKarp)算法:这个算法改进于Ford-Fulkerson算法,Ford-Fulkerson算法是不断用广搜找一条增广路,然后判断一次这条路的最小流量,再对这条路增流,而EK与FF算法不同的是用一个数组记录了广搜之后增广路的最小流量,然后再根据父亲数组去增流,时间复杂度为O(VE^2)
- typedef struct{
- int flow;
- int capacity;
- }maps;
-
- maps map[MAXV][MAXV];
-
- int maxflow;
- int sp,fp;
- int parent[MAXV];
- int vertime;
-
- int bfs(int start,int end){
- int a[MAXV],i,v;
- queue <int>q;
-
- memset(a,0,sizeof(a));
- memset(parent,-1,sizeof(parent));
-
- q.push(start);
- a[start]=MAXINT;
- while(!q.empty()){
- v=q.front();q.pop();
- for(i=1;i<=vertime;i++){
- if(!a[i] && map[v][i].capacity>map[v][i].flow){
- q.push(i);
- parent[i]=v;
- a[i]=min(a[v],map[v][i].capacity-map[v][i].flow);
- }
- }
- if(v==end) break;
- }
- return a[end];
- }
- void EdmondsKarp(){
- int i,tmp;
- maxflow=0;
- while(tmp=bfs(sp,fp)){
- for(i=fp;i!=sp;i=parent[i]){
- map[i][parent[i]].flow-=tmp;
- map[parent[i]][i].flow+=tmp;
- }
- maxflow+=tmp;
- }
- }
SAP(最短增广路):先简单的描述一下SAP的过程:
首先根据可行弧(容量>流量)分层,汇点为第0层,源点为d[s]层
........①
分层之后,不断从源点搜索增广路,按允许弧(容量>流量,并且由v指向u的一条弧满足d[v]=d[u]+1)搜索,也就是说前面的层数比后面层数
小1,这样搜索出来的一条增广路满足最短增广路。
........②
当从源点出发找不到一条增广路之后,使最后那条可行流的所指的顶点层数加1,再从源点开始搜索,即进行第②步操作。
........③
当从源点出发找到一条增广路,对这条增广路增流之后,再从源点出发找增广路,即进行第2步操作。
........④
当满足d[s]>n-1则退出搜索,因为图为n个节点,从0开始最多n-1层,如果d[s]大于n-1层,则中间出现断层,就找不到那么一条增广路了。
........⑤
这样分析这个算法的复杂度,网络中最多m条边,做多可以增广m次,用BFS增广,一次增广的复杂度为O(m+n),其中O(m)为BFS的花费,O(n)
为修改流量的花费,所以在每一个阶段寻找增广路的复杂度为O(m×(m+n))=O(m^2),因此n个阶段寻增广路的复杂度为O(n×m^2).
综上所述,最短增广路算法的总复杂度即为建层次网络的总复杂度与寻找增广路的总复杂度之和,为O(n×m^2)
- int n;
- int c[MAXV][MAXV];
- int r[MAXV][MAXV];
- int source,sink;
- int dis[MAXV],maxflow;
-
- void bfs(){
- int v,i;
- queue <int>q;
- memset(dis,0,sizeof(dis));
- q.push(sink);
- while(!q.empty()){
- v=q.front();q.pop();
- for(i=0;i<=sink;i++){
- if(!dis[i] && c[i][v]>0){
- dis[i] = dis[v] +1;
- q.push(i);
- }
- }
- }
- }
-
- void sap(){
- int top=source,pre[MAXV],i,j,low[MAXV];
-
- bfs();
- memset(low,0,sizeof(low));
- while(dis[source]
- low[source]=INF;
- for(i=0;i<=sink;i++){
- if(r[top][i]>0 && dis[top]==dis[i] +1) break;
- }
- if(i<=sink){
- low[i]=min(r[top][i],low[top]);
- pre[i]=top;top=i;
- if(top==sink){
- maxflow+=low[sink];
- j=top;
- while(j != source){
- i=pre[j];
- r[i][j]-=low[sink];
- r[j][i]+=low[sink];
- j=i;
- }
- top=source;
- }
- }
- else{
- int mindis=INF;
- for(j=0;j <=sink;j++){
- if(r[top][j]>0 && mindis>dis[j] +1)
- mindis=dis[j] +1;
- }
- dis[top]=mindis;
- if(top!=source) top=pre[top];
- }
- }
- }
运用gap优化:
即当标号中出现了不连续标号的情况时,即可以证明已经不存在新的增广流,此时的流量即为最大流。
简单证明下:
假设不存在标号为k的结点,那么这时候可以将所有的结点分成两部分,一部分为d(i)>k,另一部分为d(i) 如此分成两份,因为性质2可知,允许路为最短的增广路,又因为不存在从>k部分到
最大流。
sap+gap+邻接矩阵
- #define INF INT_MAX
- #define min(a,b) (a>b?b:a)
-
-
- int sink,source,res[MAXV][MAXV],n;
- int pre[MAXV],dis[MAXV],gap[MAXV],maxflow,cur[MAXV];
-
- int sap(){
- int s=source,t=sink;
- memset(cur,0,sizeof(cur));
- memset(dis,0,sizeof(dis));
- memset(gap,0,sizeof(gap));
-
- int u=pre[s]=s,aug=INF,v;
- maxflow=0;
- gap[source]=n;
-
- while(dis[s]
- loop:
- for(v=cur[u];v
- if(res[u][v] && dis[u]==dis[v]+1){
- cur[u]=v;
- aug=min(aug,res[u][v]);
- pre[v]=u;
- u=v;
- if(v==t){
- maxflow+=aug;
- for(u=pre[u];v!=s;v=u,u=pre[u]) res[u][v]-=aug,res[v][u]+=aug;
- aug=INF;
- }
- goto loop;
- }
-
- int mind=n;
- for(v=0;v
- if(res[u][v]&&(mind>dis[v])){
- cur[u]=v;
- mind=dis[v];
- }
-
- if((--gap[dis[u]])==0) break;
-
- gap[dis[u]=mind+1]++;
- u=pre[u];
- }
- return maxflow;
- }
sap+gap+邻接表
- typedef struct{
- int t,r,contrary,next;
- }Edge;
- Edge edge[MAXE];
- int n,m,source,sink,edge_sum,maxflow;
- int head[MAXV],dis[MAXV],cur[MAXV],gap[MAXV],pre[MAXV];
-
- void sap(){
- int u=pre[source]=source,tmp=INF,v,a;
- memset(dis,0,sizeof(dis));
- memset(gap,0,sizeof(gap));
-
- for(v=0;v<=n;v++) cur[v]=head[v];
-
- gap[source]=n;
- maxflow=0;
-
- while(dis[source]
- loop:
- for(v=cur[u];v!=-1;v=edge[v].next){
- a=edge[v].t;
- if(dis[u]==dis[a]+1 && edge[v].r>0){
- cur[u]=v;
- tmp=min(tmp,edge[v].r);
- pre[a]=u;
- u=a;
- if(u==sink){
- while(u!=source){
- u=pre[u];
- edge[cur[u]].r-=tmp;
- edge[cur[u]^1].r+=tmp;
- }
- maxflow+=tmp;
- tmp=INF;
- }
- goto loop;
- }
- }
-
- int mind=n;
- for(v=head[u];v!=-1;v=edge[v].next){
- a=edge[v].t;
- if(edge[v].r>0 && mind>dis[a]){
- cur[u]=v;
- mind=dis[a];
- }
- }
-
- if((--gap[dis[u]])==0) break;
-
- gap[dis[u]=mind+1]++;
- u=pre[u];
- }
- }
dinic算法(连续最短增广路算法):
首先dinic也用到了分层的思想,但是它与最短增广路不同的是:
SAP在每个阶段执行完一次BFS增广后,要重新启动BFS从源点到汇点找一条增广路
Dinic算法,只要一次DFS过程就能找出多条增广路,进行增广
下面简单描述下Dinic算法的过程:
首先分层,源点的层次为第0层,而汇点的层次为第d[t]层。
........①
分层之后,进行深搜,深搜的时候也是按允许弧深搜(容量>流量,并且由v指向u的一条弧满足d[v]=d[u]+1),在搜索的过程中会记录下当前的
最小流量和当前节点剩余流量,这样在回溯的时候就可以不断的找多条增广路并进行更新,因为是按照允许弧进行寻找增广路,所以也会满
足找的增广路是最短的。
........②
当深搜完毕之后,再次进行分层,即进行第①步
........③
分析dinic的复杂度,在每一个阶段,DFS遍历时前进与后退的花费为O(m×n),因为最多进行n次DFS,所以在Dinic算法中找增广路的总复杂度
为O(m×n^2)
- #define MAXV 410
- #define INF INT_MAX
- #define min(a,b) (a>b?b:a)
-
- int res[MAXV][MAXV];
- int dis[MAXV];
- int source,sink,n,maxflow;
-
- int bfs(){
- int k;
- queue<int> q;
- memset(dis,-1,sizeof(dis));
- dis[sink]=0;
-
- q.push(sink);
- while(!q.empty()){
- k=q.front();
- q.pop();
- for(int i=0;i
- if(dis[i]==-1 && res[i][k]){
- dis[i] = dis[k] + 1;
- q.push(i);
- }
- }
- if(k==source) return 1;
- }
- return 0;
- }
-
- int dfs(int cur,int cp){
- if(cur==sink) return cp;
-
- int tmp=cp,t;
- for(int i=0;i
- if(dis[i]+1==dis[cur] && res[cur][i]){
- t=dfs(i,min(res[cur][i],tmp));
- res[cur][i]-=t;
- res[i][cur]+=t;
- tmp-=t;
- }
- }
- return cp-tmp;
- }
-
- void dinic(){
- maxflow=0;
- while(bfs()) maxflow+=dfs(source,INF);
- }
邻接表:邻接表:邻接表:邻接表:邻接表:邻接表:
邻接表:
- typedef struct{
- int s,t,r,next;
- }Edge;
-
- Edge edge[MAXE];
-
- int n,m,source,sink,edge_sum,maxflow;
- int head[MAXV],dis[MAXV];
-
- int bfs(){
- int i,v,tmp;
- queue <int>q;
- memset(dis,0,sizeof(dis));
- dis[source]=1;
- q.push(source);
- while(!q.empty()){
- v=q.front();q.pop();
- for(i=head[v];i!=-1;i=edge[i].next){
- tmp=edge[i].t;
- if(!dis[tmp] && edge[i].r){
- dis[tmp]=dis[v]+1;
- if(tmp==sink) return 1;
- q.push(tmp);
- }
- }
- }
- return 0;
- }
-
- int dfs(int cur,int cp){
- if(cur==sink) return cp;
-
- int tmp=0,i,a,t;
- for(i=head[cur];i!=-1 && tmp
- a=edge[i].t;
- if(dis[a]==dis[cur]+1 && edge[i].r){
- t=dfs(a,min(edge[i].r,cp-tmp));
- edge[i].r-=t;
- edge[i^1].r+=t;
- tmp+=t;
- }
- }
- if (!tmp) dis[cur]=-1;
- return tmp;
- }
-
- void dinic(){
- maxflow=0;
- while(bfs()) maxflow+=dfs(source,INF);
- }
相对来说,EK算法和dinic算法容易实现和调试,并且dinic算法的效率也很高,所以大多数人都选择了dinic算法,但是sap的gap优化其实效
率是大于dinic的,所以有的时候卡时间可以选择sap+gap优化
最大费用最大流:
最大流问题仅注意网络流的流通能力,没有考虑流通的费用。实际上费用因素是很重要的。例如在交通运输问题中,往往要求在完成运输任
务的前提下,寻求一个使总运输费用最省的运输方案,这就是最小费用流问题。如果只考虑单位货物的运输费用,那么这个问题就变成最短
路径问题,由此可见,最短路问题是最小费用流问题的基础。现已有一系列求最短路的成功方法。 最小费用流(或最小费用最大流)问题,可
以交替使用求解最大流和最短路两种方法,通过迭代得到解决。
一般讲费用看成是求最小路径的模型,在求最短路的时候限制一下条件就可以了。
MCMF(spfa)邻接矩阵:
- int source,sink,maxflow,mincost;
- int res[MAXV][MAXV],cost[MAXV][MAXV];
- int parent[MAXV],d[MAXV];
-
- void spfa(){
- queue <int>q;
- int i,v;
- bool vis[MAXV];
- memset(parent,-1,sizeof(parent));
- memset(vis,false,sizeof(vis));
-
- for(i=source;i<=sink;i++) d[i]=INF;
- d[source]=0;
- q.push(source);
- vis[source]=true;
-
- while(!q.empty()){
- v=q.front();q.pop();
- vis[v]=false;
-
- for(i=0;i<=sink;i++){
- if(res[v][i] && d[v]+cost[v][i]
- d[i]=d[v]+cost[v][i];
- parent[i]=v;
- if(!vis[i]){
- vis[i]=true;
- q.push(i);
- }
- }
- }
- }
- }
-
- void MCMF(){
- int v,minflow;
- maxflow=0;
- while(1){
- spfa();
- if(parent[sink]==-1) break;
-
- minflow=INF;
- v=sink;
- while(parent[v]!=-1){
- minflow=min(minflow,res[parent[v]][v]);
- v=parent[v];
- }
-
- v=sink;
- while(parent[v]!=-1){
- res[parent[v]][v]-=minflow;
- res[v][parent[v]]+=minflow;
- v=parent[v];
- }
- maxflow+=minflow;
- mincost+=d[sink]*minflow;
- }
- }
MCMF(spfa)邻接表:
- #define INF INT_MAX
- #define min(a,b) (a>b?b:a)
- #define MAXV 1100
- #define MAXM 40100
-
- typedef struct{
- int s,t,next,w,r;
- }Edge;
-
- Edge edge[MAXM];
- int source,sink,n,m,mincost,edgesum;
- int head[MAXV],d[MAXV],parent[MAXV];
-
- void addedge(int a,int b,int c,int r){
- edge[edgesum].s=a;
- edge[edgesum].t=b;
- edge[edgesum].r=r;
- edge[edgesum].w=c;
- edge[edgesum].next=head[a];
- head[a]=edgesum++;
-
- edge[edgesum].s=b;
- edge[edgesum].t=a;
- edge[edgesum].r=0;
- edge[edgesum].w=-c;
- edge[edgesum].next=head[b];
- head[b]=edgesum++;
- }
-
- int spfa(){
- queue <int>q;
- int v,i,tmp;
- bool vis[MAXV];
-
- for(i=0;i<=sink;i++) d[i]=INF;
- memset(vis,false,sizeof(vis));
- memset(parent,-1,sizeof(parent));
-
- q.push(source);
- vis[source]=true;
- d[source]=0;
- while(!q.empty()){
- v=q.front();q.pop();
- vis[v]=false;
-
- for(i=head[v];i!=-1;i=edge[i].next){
- tmp=edge[i].t;
- if(edge[i].r && edge[i].w+d[v]
- d[tmp]=edge[i].w+d[v];
- parent[tmp]=i;
- if(!vis[tmp]){
- q.push(tmp);
- vis[tmp]=true;
- }
- }
- }
- }
- return 0;
- }
-
- void MCMF(){
- int u,minflow;
- mincost=0;
- while(1){
- spfa();
- if(parent[sink]==-1) break;
-
- minflow=INF;
- u=parent[sink];
- while(u!=-1){
- minflow=min(minflow,edge[u].r);
- u=parent[edge[u].s];
- }
-
- u=parent[sink];
- while(u!=-1){
- edge[u].r-=minflow;
- edge[u^1].r+=minflow;
- u=parent[edge[u].s];
- }
- mincost+=d[sink]*minflow;
- maxflow+=minflow;
- }
- }