您的位置:首页 > 理论基础 > 计算机网络

《网络流学习笔记02--Edmonds-Karp,Ford-Fulkerson,Dinic三种算法实现最大流》

2015-02-11 18:14 441 查看
【HDU3549】题目链接:click here

三种方法都用了一下,对比得出EK最少,只用46ms。

【Edmonds-Karp算法】

基础的最大流算法,每次BFS寻找最短路进行增广,找出一条残余路径就可以了。然后对残余网络进行增广,不要忘记正向增广,相当于负向减少,也要在图中保存记录。

最后求一个割集来得到最大流,效率O(VE2),“找任意路径”最简单的方法是用DFS,但是数据要稍微增加就会变得较慢,采用BFS,源点和汇点保存在s和t中,净流量保存在变量f中。

代码:

/*Edmonds-Karp算法
源点和汇点保持在s和t中,
净流量保持在变量f中
*/
#include <math.h>
#include <queue>
#include <deque>
#include <vector>
#include <stack>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
const int maxn = 20;
const int inf=0x3f3f3f3f;
int cap[20][20],flow[20][20];// 容量。流量
int Max_flow(int t)
{
    int s=1,i,j,f=0,p[20],a[20];// p代表上一个节点,a代表残量。
     mem(flow,0);
    queue<int> Q ;
    while(1)
    {
        while(!Q.empty())
            Q.pop();
        memset(a,0,sizeof(a));
        Q.push(s);
        a[s]=inf;
        while(!Q.empty())
        {
            i =Q.front();
            if(i==t)
                break;
            Q.pop();
            for(j=1; j<=t; j++)
            {
                if(i!=j&&!a[j]&&cap[i][j]>flow[i][j])
                {
                    p[j]=i;
                    Q.push(j);
                    a[j]= a[i]>cap[i][j]-flow[i][j]? cap[i][j]-flow[i][j]:a[i];
                }
            }
        }
        if(!a[t]) return f;//找不到增广路,说明此时已经是最大流了。
        for(i=t; i!=s; i=p[i])
        {
            flow[i][p[i]] -=a[t];
            flow[p[i]][i] +=a[t];
        }
        f+=a[t];
    }
}
int main()
{
    int a,b,c,n,m,ncase,cas=1;
    scanf("%d",&ncase);
    while(ncase--)
    {
        scanf("%d %d",&n,&m);
        memset(cap,0,sizeof(cap));
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a==b)
                continue;
            cap[a][b]+=c;
        }
        printf("Case %d: %d\n",cas++,Max_flow(n));
    }
    return 0;
}
【Ford-Fulkerson】

该算法是大量算法的基础,有多种实现方法。

Ford-Fulkerson算法是一种迭代算法,首先对图中所有顶点对的流大小清零,此时的网络流大小也为0。在每次迭代中,通过寻找一条“增广路径”(augument path)来增加流的值。增广路径可以看作是源点s到汇点t的一条路径,并且沿着这条路径可以增加更多的流。迭代直至无法再找到增广路径位置,此时必然从源点到汇点的所有路径中都至少有一条边的满边(即边的流的大小等于边的容量大小)。

/*Ford-Fulkerson 算法
  邻接表实现
*/
#include <ctype.h>  
#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1101;
const int inf=0x3f3f3f3f;
struct edge
{
    int to,cap,rev;
};
vector<edge > G[maxn];        //图的邻接表表示
bool used[maxn];              //访问标记
void add_edge(int from,int to,int cap)
{
    G[from].push_back((edge)
    {
        to,cap,G[to].size()
    });
    G[to].push_back((edge)
    {
        from,0,G[from].size()-1
    });
}
int dfs(int v,int t,int f)
{
    if(v==t) return f;
    used[v]=true;
    for(int i=0; i<G[v].size(); i++)
    {
        edge &e=G[v][i];
        if(!used[e.to]&&e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    for(;;)
    {
        memset(used,0,sizeof(used));
        int f=dfs(s,t,inf);
        if(f==0) return flow;
        flow+=f;
    }
}
int main()
{
    int n,m,too,capp,revv;
    int tt,j=1;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&m,&n);
        memset(G,0,sizeof(G));
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d",&too,&capp,&revv);
            add_edge(too,capp,revv);
        }
        printf("Case %d: %d\n",j++,max_flow(1,m));
    }
    return 0;
}
【Dinic 算法】

网络流最大流的优化算法之一,每一步对原图进行分层,然后用DFS求增广路。时间复杂度是O(n^2*m) 。

算法流程

1、根据残量网络计算层次图。

2、在层次图中使用DFS进行增广直到不存在增广路。

3、重复以上步骤直到无法增广。

/*Dinic 算法
总是寻找最短路,并沿这条路增广*/
#include <math.h>
#include <queue>
#include <deque>
#include <vector>
#include <stack>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
const int maxn = 18;
const int inf=0x3f3f3f3f;
struct edge
{
    int from, to, cap;
};
vector<edge> EG;
vector<int> G[maxn];
int n, s, t, ans;
int  level[maxn];          //顶点到源点的距离标号
int  cur[maxn];         //当前弧,在其之前的边已经没有用了
void add_edge(int from, int to, int cap)
{
    EG.push_back((edge)
    {
        from, to, cap
    });
    EG.push_back((edge)
    {
        to, from, 0
    });
    G[from].push_back(EG.size()-2);
    G[to].push_back(EG.size()-1);
}

bool bfs()
{
    mem(level,-1);
    queue<int> que;
    que.push(s);
    level[s] = 0;
    while(!que.empty())
    {
        int v = que.front();
        que.pop();
        for(int i = 0; i < G[v].size(); i++)
        {
            edge& e = EG[G[v][i]];
            if(level[e.to] == -1 && e.cap > 0)
            {
                level[e.to] = level[v]+1;
                que.push(e.to);
            }
        }
    }
    return (level[t]!=-1);
}

int dfs(int v, int a)
{
    if(v == t || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[v]; i < G[v].size(); i++)
    {
        edge& e = EG[G[v][i]];
        if(level[v]+1 == level[e.to] && (f = dfs(e.to, Min(a, e.cap))) > 0)
        {
            e.cap -= f;
            EG[G[v][i]^1].cap += f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
void Dinic()
{
    ans = 0;
    while(bfs())
    {
        mem(cur,0);
        ans += dfs(s, inf);
    }
}
int main()
{
    int T, m, from, to, cap,cas=1;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        while(m--)
        {
            scanf("%d%d%d", &from, &to, &cap);
            add_edge(from, to, cap);
        }
        s = 1, t = n;
        Dinic();
        printf("Case %d: %d\n", cas++, ans);
        EG.clear();
        for(int i = 0; i <= n; ++i)
            G[i].clear();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: