网络流入门总结(EK算法)

终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着LRJ的白书,看了好久,感觉可以了,然并卵,比赛的时候就签了一下到,就走了。。。从那以后就没碰过网络流,原来等着跟大工大神学习的,好像因为各种事情一直没有上这一课。然后又把白皮看了一遍,看了很多博客,感觉其中有一个人的博客写的特别好,现在找不到了。。。

跟着专题训练写了几道模板题,照着大白书稍微改了一点,成为了自己的模板。下面做一些入门网络流的总结:

1:反向边。每一条路径都对应着一条反向边,u->v流过f的流量,那个对应的反向边v->u流过-f的流量。而对应的反向边的容量cap = 0。

2:残量网络。就是把每一条路径剩余容量标称着条边的权值。(当然反向边也是必须要算的,这个时候要注意边的数量是原来边数量的两倍,权值>0才能形成一条边)

3:增广路。如果在残余网络中有一条路径能够从s(源点)走到t(汇点)那么,我们就称这条路为增广路。

4:增广路求最大流。增广路对应的每一条路径权值的最小值(d)全部加到原网络时,这个时候我们发现总的流量增加了d,(注意在d添加到原边时,对应的反向边的流量需要-d,因为是对应的关系,很好理解)。那么问题就转化成如何找到所有的增广路。这个就很显然了,我们只要遍历一遍残量网络图,就可以找到了。(BFS实现,DFS比BFS慢一点,有一个人的博客有测试的)

下面就说一个这6道模板题:

POJ_1273  http://poj.org/problem?id=1273 很裸的最大流题,正好可以检测模板的正确性:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 210
using namespace std;

struct Edge{
    int from,to,cap,flow;
    Edge(){};
    Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edges[N<<1];

int edge_cnt;
vector <int> G[N]; //存图
int pre[N],a[N]; //记录增广路路径
int s,t,n,m;
bool vis[N];

void AddEdge(int from,int to,int cap){
    edges[edge_cnt++] = Edge(from,to,cap,0);
    edges[edge_cnt++] = Edge(to,from,0,0);
    G[from].push_back(edge_cnt - 2);
    G[to].push_back(edge_cnt -1);
}

int bfs(){
    memset(vis,false,sizeof(vis));
    queue <int> q;
    q.push(s);
    a[s] = INF;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t)  break;
        FOR(i,0,G[u].size()){
            int e = G[u][i];
            int v = edges[e].to;
            if(!vis[v] && edges[e].cap > edges[e].flow){
                vis[v] = true;
                pre[v] = e;
                a[v] = min(a[u],edges[e].cap-edges[e].flow);
                q.push(v);
            }
        }
    }
    if(!vis[t]) return -1;
    return a[t];
}

int EK(){
    int max_flow = 0;
    int tem;
    while((tem = bfs()) != -1){
        max_flow += tem;
        for(int u = t; u != s; u = edges[pre[u]].from){
            edges[pre[u]].flow += tem;
            edges[pre[u]^1].flow -= tem;
        }
    }
    return max_flow;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        FOR(i,0,N)  G[i].clear();
        s = 1; t = m;
        int u,v,c;
        edge_cnt = 0;
        FOR(i,0,n){
            scanf("%d%d%d",&u,&v,&c);
            AddEdge(u,v,c);
        }
        printf("%d\n",EK());
    }
    return 0;
}

HDU_3549  http://acm.hdu.edu.cn/showproblem.php?pid=3549 也是一个模板练手题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 1010
using namespace std;

struct Edge{
    int from,to,cap,flow;
    Edge(){};
    Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edges[N<<1];

int edge_cnt;
vector <int> G[N]; //存图
int pre[N],a[N]; //记录增广路路径
int s,t,n,m;
bool vis[N];

void AddEdge(int from,int to,int cap){
    edges[edge_cnt++] = Edge(from,to,cap,0);
    edges[edge_cnt++] = Edge(to,from,0,0);
    G[from].push_back(edge_cnt - 2);
    G[to].push_back(edge_cnt -1);
}

int bfs(){
    memset(vis,false,sizeof(vis));
    queue <int> q;
    q.push(s);
    a[s] = INF;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t)  break;
        FOR(i,0,G[u].size()){
            int e = G[u][i];
            int v = edges[e].to;
            if(!vis[v] && edges[e].cap > edges[e].flow){
                vis[v] = true;
                pre[v] = e;
                a[v] = min(a[u],edges[e].cap-edges[e].flow);
                q.push(v);
            }
        }
    }
    if(!vis[t]) return -1;
    return a[t];
}

int EK(){
    int max_flow = 0;
    int tem;
    while((tem = bfs()) != -1){
        max_flow += tem;
        for(int u = t; u != s; u = edges[pre[u]].from){
            edges[pre[u]].flow += tem;
            edges[pre[u]^1].flow -= tem;
        }
    }
    return max_flow;
}

int main()
{
    //freopen("test.in","r",stdin);
    int T,tCase = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        FOR(i,0,N)  G[i].clear();
        s = 1; t = n;
        int u,v,c;
        edge_cnt = 0;
        FOR(i,0,m){
            scanf("%d%d%d",&u,&v,&c);
            AddEdge(u,v,c);
        }
        printf("Case %d: ",++tCase);
        printf("%d\n",EK());
    }
    return 0;
}

POJ_1087 http://poj.org/problem?id=1087 这个题注意一下对应关系就好了,题目是插头可以变成别的种类的,不是插座可以变成别的种类的,我用的map来对应的节点,建图的时候麻烦一点。(其实也可以用二分图的匹配来写,本来二分图就可以用网络流来写的,所以不影响)在插头一边加源点,插座一边加汇点,转换器对应的路径,cap=1。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <string>
#include <map>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 440

using namespace std;

struct Str{
    char s[30];
    Str() {}
    Str(char* ths) {FOR(i,0,30) s[i] = ths[i];}
    bool operator < (const Str& rhs) const{
        return strcmp(s,rhs.s) == -1 ? true : false;
    }
}str[N];

int n,m,k;
char src[N][30],tar[N][30],sr[N][30],ta[N][30];
int cnt,co;
int l[N],r[N];
bool mat[N][N];
bool vis[N];
vector <int> GP[N];
map <string,int> p;

void dfs(int u){
    FOR(i,0,GP[u].size()){
        int v = GP[u][i];
        if(!vis[v]) {vis[v] = true;dfs(v);}
    }
}

///ÍøÂçÁ÷Ïà¹Ø
int s,t,edge_cnt;
int a[N];

struct Edge{
    int from,to,cap,flow;
    Edge() {}
    Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N*2];

vector <int> G[N];
int pre[N];

void AddEdge(int from,int to,int cap){
    edge[edge_cnt++] = Edge(from,to,cap,0);
    edge[edge_cnt++] = Edge(to,from,0,0);
    G[from].push_back(edge_cnt-2);
    G[to].push_back(edge_cnt-1);
}

int bfs(){
    memset(vis,false,sizeof(vis));
    queue <int> q;
    a[s] = INF;
    q.push(s);
    vis[s] = true;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t)  break;
        FOR(i,0,G[u].size()){
            int e = G[u][i];
            int v = edge[e].to;
            if(!vis[v] && edge[e].cap > edge[e].flow){
                a[v] = min(a[u],edge[e].cap-edge[e].flow);
                pre[v] = e;
                vis[v] = true;
                q.push(v);
            }
        }
    }
    if(!vis[t]) return -1;
    return a[t];
}

int EK(){
    int max_flow = 0;
    int d;
    while((d = bfs()) != -1){
        max_flow += d;
        for(int u = t;u != s;u = edge[pre[u]].from){
            edge[pre[u]].flow += d;
            edge[pre[u]^1].flow -= d;
        }
    }
    return max_flow;
}

/****/

void init(){
    sort(str,str+co);
    cnt = 0;
    p[str[0].s] = (++cnt);
    FOR(i,1,co){
        if(strcmp(str[i].s,str[i-1].s) != 0) p[str[i].s] = (++cnt);
    }
    FOR(i,0,N)  GP[i].clear();
    FOR(i,0,k){
        GP[p[sr[i]]].push_back(p[ta[i]]);
        //printf("%d %d\n",p[sr[i]],p[ta[i]]);
        //printf("%d %d\n",p[sr[i]],GP[p[sr[i]]].size());
    }
    memset(mat,false,sizeof(mat));
    FOR(i,1,cnt+1){
        memset(vis,false,sizeof(vis));
        vis[i] = true;
        dfs(i);
        FOR(j,1,cnt+1){
            if(vis[j]) mat[i][j] = true;
        }
    }
    FOR(i,n+1,n+m+1){
        l[i] = p[tar[i]];
    }
    FOR(i,1,n+1){
        r[i] = p[src[i]];
    }
    s = 0;
    t = n+m+1;
    FOR(i,0,N)  G[i].clear();
    edge_cnt = 0;
    FOR(i,n+1,n+m+1){
        AddEdge(s,i,1);
    }
    FOR(i,1,n+1){
        AddEdge(i,t,1);
    }
    FOR(i,n+1,n+m+1){
        FOR(j,1,n+1){
            if(mat[l[i]][r[j]]){AddEdge(i,j,1);}
        }
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d",&n)){
        co = 0;
        FOR(i,1,n+1){
            scanf("%s",src[i]);
            str[co++] = Str(src[i]);
        }
        scanf("%d",&m);
        char tem[30];
        FOR(i,n+1,n+m+1){
            scanf("%s%s",tem,tar[i]);
            str[co++] = Str(tar[i]);
        }
        scanf("%d",&k);
        FOR(i,0,k){
            scanf("%s%s",sr[i],ta[i]);
            str[co++] = Str(sr[i]);
            str[co++] = Str(ta[i]);
        }
        init();
        printf("%d\n",m-EK());
    }
    return 0;
}
POJ_1274 http://poj.org/problem?id=1274 本题是一个裸的二分图最大匹配,正好练一下二分图最大匹配的模板。网络流当然也可以写,一边加源点,一边加汇点,容量都设为1,就变成了一个最大流问题。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <algorithm>
#include <vector>
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 250

using namespace std;

vector <int> G[N<<1];
int n,m;
int march[N<<1];
bool vis[N<<1];

bool dfs(int u){
    vis[u] = true;
    FOR(i,0,G[u].size()){
        int v = G[u][i];
        if(!vis[v]){
            vis[v] = true;
            if(march[v] == -1 || dfs(march[v])){
                march[v] = u;
                march[u] = v;
                return true;
            }
        }
    }
    return false;
}

int MaxMarch(){
    int ans = 0;
    memset(march,-1,sizeof(march));
    FOR(i,1,n+1){
        if(march[i] == -1){
            memset(vis,false,sizeof(vis));
            if(dfs(i))  ans++;
        }
    }
    return ans;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        FOR(i,0,(n+m+1))   G[i].clear();
        int cnt;
        int v;
        FOR(i,1,n+1){
            scanf("%d",&cnt);
            while(cnt--){
                scanf("%d",&v);
                G[i].push_back(v+n);
                G[v+n].push_back(i);
            }
        }
        printf("%d\n",MaxMarch());
    }
    return 0;
}

POJ_1459 http://poj.org/problem?id=1459 题目乍一看有很多源点,汇点,其实都是假的,我们把所有源点全部连到一个s上,对应的路径cap就是这个点的cap,同理,所有的汇点都连到t上,cap对应的就是这个点的cap。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define INF (1<<30)
#define N 220

using namespace std;

inline void readint(int &ret)
{
    char c;
    do { c = getchar();
    } while(c < '0' || c > '9');
    ret = c - '0';
    while((c=getchar()) >= '0' && c <= '9')
    ret = ret * 10 + ( c - '0' );
}

int n,np,nc,m;


///wangluoliu xiang guan
struct Edge{
    int from,to,cap,flow;
    Edge(){}
    Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N];

int s,t,edge_cnt,a[N],pre[N];
bool vis[N];
vector <int> G[N];

void AddEdge(int u,int v,int c){
    G[u].push_back(edge_cnt);
    edge[edge_cnt++] = Edge(u,v,c,0);
    G[v].push_back(edge_cnt);
    edge[edge_cnt++] = Edge(v,u,0,0);
}

void init_ntf(){
    FOR(i,0,N)  G[i].clear();
    edge_cnt = 0;
}

int bfs(){
    memset(vis,false,sizeof(vis));
    queue <int> q;
    q.push(s);
    a[s] = INF;
    vis[s] = true;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t)  break;
        FOR(i,0,G[u].size()){
            int e = G[u][i];
            int v = edge[e].to;
            if(!vis[v] && edge[e].cap > edge[e].flow){
                a[v] = min(a[u],edge[e].cap - edge[e].flow);
                pre[v] = e;
                vis[v] = true;
                q.push(v);
            }
        }
    }
    if(!vis[t]) return -1;
    return a[t];
}

int EK(){
    int max_flow = 0;
    int d;
    while((d = bfs()) != -1){
        max_flow += d;
        for(int u = t; u != s; u = edge[pre[u]].from){
            edge[pre[u]].flow += d;
            edge[pre[u]^1].flow -= d;
        }
    }
    return max_flow;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d%d%d",&n,&np,&nc,&m)){
        s = 0;
        t = n+1;
        int u,v,l;
        init_ntf();
        FOR(i,0,m){
            readint(u);
            readint(v);
            readint(l);
            AddEdge(u+1,v+1,l);
        }
        FOR(i,0,np){
            readint(v);
            readint(l);
            AddEdge(s,v+1,l);
        }
        FOR(i,0,nc){
            readint(u);
            readint(l);
            AddEdge(u+1,t,l);
        }
        printf("%d\n",EK());
    }
    return 0;
}

POJ_3281  http://poj.org/problem?id=3281  拆点+最大流。单纯地以为是二分图匹配肯定就贵了(反正我是没想出来二分图要怎么搞)。放到网络流里面,就会发现图建不出来!!!为什么???因为牛这个点可以经过无数遍,这个与题目要求显然不符合,这个时候要怎么办,就是把一头牛拆成牛1,牛2,对应的牛1,牛2之间连接一条容量为1的路径。这个样子就能保证每一头牛都最多走了一次。。。见图的关键就是每一条路径都与每一条路径实际一一对应!!!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#define INF (1<<30)
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 440

using namespace std;

struct Edge{
    int from,to,cap,flow;
    Edge() {}
    Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N*2];

vector <int> G[N];
int edge_cnt,pre[N],a[N],f,d,n,s,t;
bool vis[N];

void AddEdge(int u,int v,int c){
    edge[edge_cnt++] = Edge(u,v,c,0);
    edge[edge_cnt++] = Edge(v,u,0,0);
    G[u].push_back(edge_cnt-2);
    G[v].push_back(edge_cnt-1);
}

int bfs(){
    memset(vis,false,sizeof(vis));
    queue <int> q;
    q.push(s);
    vis[s] = true;
    a[s] = INF;
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(u == t)  break;
        FOR(i,0,G[u].size()){
            int e = G[u][i];
            int v = edge[e].to;
            if(!vis[v] && edge[e].cap > edge[e].flow){
                a[v] = min(a[u],edge[e].cap - edge[e].flow);
                pre[v] = e;
                vis[v] = true;
                q.push(v);
            }
        }
    }
    if(!vis[t]) return -1;
    return a[t];
}

int EK(){
    int max_flow = 0;
    int d;
    while((d = bfs()) != -1){
        max_flow += d;
        for(int u = t; u != s; u = edge[pre[u]].from){
            edge[pre[u]].flow += d;
            edge[pre[u]^1].flow -= d;
        }
    }
    return max_flow;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d%d",&n,&f,&d)){
        //if(n == 0 || f == 0 || d == 0)  {printf("0\n");continue;}
        FOR(i,0,N)  G[i].clear();
        edge_cnt = 0;
        s = 0;
        t = f+n+n+d+1;
        FOR(i,1,n+1){
            int cnt_f,cnt_d;
            scanf("%d%d",&cnt_f,&cnt_d);
            FOR(j,0,cnt_f){
                int u;
                scanf("%d",&u);
                AddEdge(u,f+i,1);
            }
            AddEdge(f+i,f+i+n,1);
            FOR(j,0,cnt_d){
                int v;
                scanf("%d",&v);
                AddEdge(f+i+n,f+n+n+v,1);
            }
        }
        FOR(i,1,f+1){
            AddEdge(s,i,1);
        }
        FOR(i,f+n+n+1,t){
            AddEdge(i,t,1);
        }
        printf("%d\n",EK());
    }
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-08-04 23:33  hqwhqwhq  阅读(275)  评论(0编辑  收藏  举报