图论五:网络流

参考文章一:https://blog.csdn.net/txl199106/article/details/64441994

参考文章二:https://blog.csdn.net/ergedathouder/article/details/55001645

 

一、基本概念

1、源点:只进不出的点,通常称为S

2、汇点:只出不进的点,通常称为T

3、容量&流量:容量是每条边上最大能经过的值,流量是当前经过边的值。(流量一定小于容量)

容量用C表示,流量用F表示。

4、三个基本性质:

(1)每条边的流量小于容量,即C<F

(2)流量守恒:Σ F<v,x> = Σ F<x,u>

(3)斜对称性:F<x,y> = - F<y,x>

5、容量网络&流量网络&残留网络

残留网路 = 容量网络 - 流量网络

6、割集:(可以分为点割集和边割集)

就是去掉这一部分图不连通的部分。

(最小割集是能成为割集的最小集合)

 

7、最大流最小割定理

(1)任意一个流都小于一个割;

(2)将满流边作为一个割,就构造出和最大流相等的割;

(3)最大流等于最小割

设相等的流和割分别为Fm,Cm,

则有:F<=Fm=Cm<=C(C,F为任意的)。

三个等价条件:

对于一个图G=(V,E)有源点s和汇点t

(1)F是图G最大流

(2)残留网络GF不存在增广路径,

(3)对于G的一个割(S,T),存在f=C(S,T)。

 

二、算法实现

 1、增广路径算法(EK算法)

(1)算法功能:求增广路径

(2)算法思想:bfs

(3)算法流程:

首先建立邻接表,存储每条边的数据,pre数组记录路径,vis数组标记路径,接着对数据进行初始化;

其次,bfs遍历寻找最小边,然后更新整个图,建立反向边,并求和,更新ans的值,之后一直重复此操作。

最后,如果bfs遍历时找不到最后的节点,说明图中没有增广路径,算法结束。

 

代码:

(1)邻接数组实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 1200;
const int INF = 0x3fff;
int pre[maxn],p[maxn],vis[maxn],edge[maxn][maxn],m,n;
int MIN(int x,int y){ return x<y?x:y; }
void Init() { memset(edge,0,sizeof(edge)); } 
int EK(int st,int ed) 
{
    int ans=0,j,i,fg,mi;
    while(1)
    {
        fg=0;
        queue <int> q;
        q.push(st);
        memset(vis,0,sizeof(vis)); 
        memset(pre,0,sizeof(pre));
        p[st]=INF;
        while(!q.empty()) //寻找增广路径 
        {
            int top=q.front();
            q.pop();
            for(i=1;i<=n;i++)
            if(edge[top][i]&&vis[i]==0)
            {
                vis[i]=1;
                pre[i]=top; //记录增广路径 
                if(i==ed){ fg=1;break; }
                q.push(i);
            }
        }
        if(fg==0) return ans;
        mi=INF;
        for(i=ed;i!=st;i=pre[i]) mi=MIN(mi,edge[pre[i]][i]); //寻找最小值 
        for(i=ed;i!=st;i=pre[i]) edge[pre[i]][i]-=mi,edge[i][pre[i]]+=mi; //更新路径 
        ans+=mi;
    }
} 
int main(void)
{
    int x,y,z;
    cin>>n>>m;
    Init();
    for(int i=1;i<=m;i++) cin>>x>>y>>z,edge[x][y]+=z;
    cout<<EK(1,n)<<endl;
    return 0;
} 
View Code

 

(2)邻接表实现

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 1200;
const int INF = 0x3fff;
struct Edge{
    int v,next,w; //v表示边的终点,next表示下一个边的编号 
}edge[maxn];
struct Vertex{
    int v,id; //v表示下一个点,id表示当前的点 
}pre[maxn];
int n,m,cnt;
int vis[maxn],head[maxn];
int MIN(int x,int y){ return x<y?x:y; }
void Init() //初始化 
{
    cnt=0;
    memset(edge,0,sizeof(edge));
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int w) //添加新边 
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
int EK(int st,int ed) //EK算法具体实现 
{
    int i,j,fg,tmp,ans=0,mi;
    while(1)
    {
        fg=0;st=1;ed=n;
        memset(vis,0,sizeof(vis));
        memset(pre,-1,sizeof(pre));
        pre[st].v=st;vis[st]=1;
        queue <int> q;
        q.push(st);
        while(!q.empty())
        {
            int top=q.front();
            q.pop();
            for(i=head[top];i+1;i=edge[i].next)
            {
                int v=edge[i].v;
                if(vis[v]==0&&edge[i].w)
                {
                    vis[v]=1;
                    pre[v].v=top;
                    pre[v].id=i;
                    if(v==ed) { fg=1;break; }
                    q.push(v);
                }
            }
        }
        if(fg==0) return ans;
        mi=INF;
        for(i=ed;i!=st;i=pre[i].v) mi=MIN(mi,edge[pre[i].id].w); //正向路径更新 
        for(i=ed;i!=st;i=pre[i].v) edge[pre[i].id].w-=mi,edge[pre[i].id^1].w+=mi; //a^1表示取反,即pre[i].id的反向路径更新 
        ans+=mi;
    }
}
int main(void)
{
    int x,y,z;
    cin>>n>>m;
    Init();
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        add(x,y,z);add(y,x,0);
    }
    cout<<EK(1,n)<<endl;
    return 0;
} 

/*
4 5
1 2 2
1 4 2
2 4 3
2 3 4
4 3 4
*/
View Code

 

 

2、dinic算法

算法流程:

(1)初始化,计算出剩余图

(2)根据剩余图计算层次图,若汇点不在层次图中,算法结束

(3)层次图内用dfs计算增广路径

(4)返回(2)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 1200;
const int INF = 0x3ffffff;
struct Node{
    int v,c,next;
}edge[maxn*maxn<<1];
int level[maxn],p[maxn],n,m,S,T,cnt;
void Init()
{
    memset(p,-1,sizeof(p));
    cnt=0;S=1;T=n;
}
int MIN(int x,int y)
{
    return x<y?x:y;
}
void Insert(int u,int v,int c)
{
    edge[cnt].v=v;
    edge[cnt].c=c;
    edge[cnt].next=p[u];
    p[u]=cnt++;
}
void addedge(int u,int v,int c)
{
    Insert(u,v,c); 
    Insert(v,u,0);
}
bool bfs()
{
    memset(level,-1,sizeof(level));
    level[S]=0;
    queue <int> q;
    q.push(S);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=p[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].c>0&&level[v]==-1)
            {
                level[v]=level[u]+1;
                q.push(v);
            }
        }
    }
    return level[T]!=-1;
}
int dfs(int u,int cp)
{
    if(u==T) return cp;
    int res=0;
    for(int i=p[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(level[u]+1==level[v]&&edge[i].c>0)
        {
            int t=dfs(v,MIN(cp,edge[i].c));
            cp-=t;
            res+=t;
            edge[i].c-=t;
            edge[i^1].c+=t;
            if(cp==0) break;
        }
    }
    if(res==0) level[u]=-1;
    return res;
}
int dinic()
{
    int sum=0,tmp;
    while(bfs())
    sum+=dfs(S,INF);
    return sum;
}
int main(void)
{
    int i,x,y,z;
    scanf("%d%d",&n,&m);
    Init();
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
    }
    printf("%d\n",dinic());
    return 0;
}
/*
4 5
1 2 2
1 4 2
2 4 3
2 3 4
4 3 4
*/
View Code

 

posted @ 2018-12-24 19:45  麟阁  阅读(286)  评论(0编辑  收藏  举报