网络流初步

POJ1273

参考:http://www.cnblogs.com/jackge/archive/2013/04/10/3012182.html

裸的最大流题目,要注意重边。

int map[][] ,记录容量

int flow[][]记录流量

int res[]记录残余流量

int pre[]记录父亲节点,更新flow数组是用到

从流量为0开始找增广路,清空flow数组开始。

每次将残余流量数组清空,只有src源点的残余容量是无穷大。

用BFS寻找随机路径,并且同时更新res(残余流量数组)取最小值。

如果能更新到des,也就是 res[des]不等于0 ,则 说明找到了一条增广路,把它加到max_flow里;

然后更新整个网络,也就是flow数组。正流量增加 res[des],反向流量减少res[des];

不断重复上述过程,直到res[des] == 0,找不到增广路,也就是当前已经达到了最大流。(增广路定理)

Edmond-Karp算法

#include <iostream>
#include <queue>
#include <string.h>
#include <cstdio>
#define N 250
#define INF 0x3f3f3f3f
using namespace std;
int map[N][N]; // 容量
int flow[N][N] ; //流量
int res[N];//每条边的残余流量
int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到
int n,m,max_flow;
int EK(int src,int des){
    memset(flow,0,sizeof(flow)); // start from zero_flow;
    max_flow = 0; //set max_flow to zero
    queue<int>Q;
    while(1){
        memset(res,0,sizeof(res)); // set remains  to zero;
        res[src] = INF; // start point has infinite flow;
        Q.push(src);
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            for(int v=1;v<=m;v++){
                if(!res[v] && map[u][v] > flow[u][v]){
                    pre[v] = u;
                    Q.push(v);
                    res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res
                    //都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v];
                }
            }
        }
        if(res[des] == 0) return max_flow; //找不到增广路
        for(int v=des;v!=src;v=pre[v]){
            flow[pre[v]][v] += res[des];
            flow[v][pre[v]] -= res[des]; //更新反向弧
        }
        max_flow += res[des];
    }
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        int from,to,w;
        memset(map,0,sizeof(map));
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&from,&to,&w);
            map[from][to] += w; //重边
        }
        int ans = EK(1,m);
        printf("%d\n",ans);
    }
    return 0;
}

POJ1459 多源点,多汇点

EK

处理多个源点可以通过增加一个超级源点,连接到多个源点。

同理增加一个超级汇点,让所有汇点连接到超级汇点。 可以参照算法竞赛入门经典。

#include <iostream>
#include <queue>
#include <string.h>
#include <cstdio>
#define N 110
#define INF 0x3f3f3f3f
using namespace std;
int map[N][N]; // 容量
int flow[N][N] ; //流量
int res[N];//每条边的残余流量
int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到
int n,np,nc,m,max_flow;
int src = 101,des=102;
int EK(int src,int des){
    memset(flow,0,sizeof(flow)); // start from zero_flow;
    max_flow = 0; //set max_flow to zero
    queue<int>Q;
    while(1){
        memset(res,0,sizeof(res)); // set remains  to zero;
        res[src] = INF; // start point has infinite flow;
        Q.push(src);
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            for(int v=0;v<=n+1;v++){ // 加入超级源点 汇点 n,n+1
                if(!res[v] && map[u][v] > flow[u][v]){
                    pre[v] = u;
                    Q.push(v);
                    res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res
                    //都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v];
                }
            }
        }
        if(res[des] == 0) return max_flow; //找不到增广路
        for(int v=des;v!=src;v=pre[v]){
            flow[pre[v]][v] += res[des];
            flow[v][pre[v]] -= res[des]; //更新反向弧
        }
        max_flow += res[des];
    }
}
int main(){
    while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){
        char s[10];
        memset(map,0,sizeof(map));
        src = n,des = n+1;
        int from,to,w; char ch;
        for(int i=0;i<m;i++){
            scanf(" %c%d%c%d%c%d",&ch,&from,&ch,&to,&ch,&w);
            map[from][to] = w;
        }
        for(int i=0;i<np;i++){
            scanf(" %c%d%c%d",&ch,&from,&ch,&w);
            map[src][from] = w; //连接超级源点
        }
         for(int i=0;i<nc;i++){
            scanf(" %c%d%c%d",&ch,&from,&ch,&w);
            map[from][des] = w; //连接超级汇点
        }
        int ans = EK(src,des);
        printf("%d\n",ans);
    }
    return 0;
}


NYOJ677

做了这个题才知道最大流最小割定理是多么神奇,这个题就是裸求图的最小割,然后我们直接转化为最大流问题处理就行了。

关键在于构图,增添一个超级源点0,将超级源点与间谍所在节点连接,汇点是n。其余的结点相连的权值都设置为1。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
int spy[222];
int map[222][222];
int n,m,q;
int res[222];
int flow[222][222];
int p[222];
int EK(int src,int des){
    int max_flow = 0;
    memset(flow,0,sizeof(flow));
    queue<int>Q;
    while(1){
        memset(res,0,sizeof(res));
        res[src] = INF;
        Q.push(src);
        while(!Q.empty()){
            int u = Q.front();Q.pop();
            for(int v = 0;v<=n;v++){
                if(!res[v] && map[u][v] > flow[u][v]){
                    p[v] = u;
                    res[v] = min(res[u] , map[u][v] - flow[u][v]);
                    Q.push(v);
                }
            }
        }
       // cout<<res[des]<<endl;
        if(res[des] == 0) return max_flow;
        max_flow += res[des];
        for(int v=des;v!=src;v=p[v]){
            flow[p[v]][v] += res[des];
            flow[v][p[v]] -= res[des];
        }
    }
}
int main()
{
    int T ; scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%d%d%d",&n,&m,&q);
        int po;
        memset(map,0,sizeof(map));
        for(int i=0;i<q;i++){
            scanf("%d",&po);
            map[0][po] = INF; //超级源点
        }
        int a,b;
        for(int i=0;i<m;i++){
            scanf("%d%d",&a,&b);
            map[a][b] = map[b][a] = 1;
        }
        int ans = EK(0,n);
        printf("Case #%d: %d\n",cas,ans);
    }
    return 0;
}



你可能感兴趣的:(网络流)