最大流算法总结

来源:互联网 发布:物理机安装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来寻找阻塞流。下面给出其算法步骤:
  1. 初始化流量,计算出剩余图  
  2. 根据剩余图计算层次图。若汇点不在层次图内,则算法结束  
  3. 在层次图内用一次dfs过程增广  
  4. 转步骤2  
  1. 用BFS建立分层图  注意:分层图是以当前图为基础建立的,所以要重复建立分层图
  2. 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数
  3. 重复步骤2,直到DFS找不到新的路径时,重复步骤1

注意(可以无视):

  • Dinic(其实其他的好多)算法中寻找到增广路后要将反向边增加I
  • Dinic中DFS时只在分层图中DFS,意思是说DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1,例如在图1中第一个次DFS中,1->2->4 这条路径是不合法的,因为Dis[2]=1;Dis[4]=1;

下面是dfs的过程:
ps;While outdegree(s)>0    up.top;    if u<>t        if outdegree(u)>0            设(u,v)为层次图中的一条边;            pp,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;}


原创粉丝点击