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

来源:互联网 发布:条件选股软件 编辑:程序博客网 时间:2024/06/05 03:04

终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着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 210using 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 1010using 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 440using 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 250using 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 220using 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 guanstruct 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 440using 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;}


0 0