网络流初步

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;
}

贴一个Dinic的模板
POJ的数据测出来,Dinic比EK还是快了好多。




而之外的两个题的数据反而测出Dinic更慢 - -
【Dinic poj1459】
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 111
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
    int from,to,cap,flow;
};
struct Dinic{
    int n,m,s,t; //节点数,边数(包括反向弧),源点,汇点
    vector<Edge>edges;
    vector<int>G[N];//表示节点i 的第j条边在e数组的编号
    bool vis[N]; // bfs中
    int d[N];//起点到i的距离
    int cur[M]; //当前弧下标

    bool BFS(){
        mem(vis,0);
        queue<int >Q;
        Q.push(s); d[s] = 0;vis[s] = 1;
        while(!Q.empty()){
            int x = Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge &e = edges[G[x][i]];
                if(!vis[e.to] && e.cap > e.flow){//只考虑残量网络中的弧
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x == t || a == 0) return a;
        int flow = 0,f;
        for(int &i = cur[x];i<G[x].size();i++){
            Edge&e = edges[G[x][i]];
            if(d[x] + 1 ==d[e.to] && (f = DFS(e.to,min(a,e.cap - e.flow))) > 0){
                e.flow += f;
                edges[G[x][i]^1].flow -= f;// 反向弧
                flow += f;
                a-=f;
                if(a == 0) break;
            }
        }
        return flow;
    }
    ll MaxFlow(int s,int t){
        this->s = s;this->t = t;
        ll flow = 0;
        while(BFS()){
            mem(cur,0);
            flow += DFS(s,INF);
        }
        return flow;
    }
    void addEdge(int from,int to,int cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    void init(){
        edges.clear();
        for(int i=0;i<=N;i++){
            G[i].clear();d[i] =0; vis[i] = 0;
        }
        mem(cur,0);
    }
}solve;
int main(){
    int n,np,nc,m;
    while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){
        char s[10];
        solve.init();
        int 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);
            solve.addEdge(from,to,w);
        }
        for(int i=0;i<np;i++){
            scanf(" %c%d%c%d",&ch,&from,&ch,&w);
            solve.addEdge(src,from,w); //连接超级源点
        }
         for(int i=0;i<nc;i++){
            scanf(" %c%d%c%d",&ch,&from,&ch,&w);
            solve.addEdge(from,des,w); //连接超级汇点
        }
        printf("%I64d\n",solve.MaxFlow(src,des));
    }
    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;
}

HDU3987
求图的最小割,并且要求割去的边数最少。
通过最小割最大流定理转化成最大流问题。
设置N为大于节点数的一个数。
加权的时候直接将 (新权值) = (权值*N + 1)添入节点中。(注意加的1代表一条边,这样就可以通过取余来算边数了)
这样用原来的方法算出来ans。ans/N 就是最大流, ans%N就是最大流经过的边了。(神构造啊!)
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define N 1110
#define M 111*100
typedef long long ll;
using namespace std;
struct Edge{
    int from,to,cap,flow;
};
struct Dinic{
    int n,m,s,t; //节点数,边数(包括反向弧),源点,汇点
    vector<Edge>edges;
    vector<int>G[N];//表示节点i 的第j条边在e数组的编号
    bool vis[N]; // bfs中
    int d[N];//起点到i的距离
    int cur[M]; //当前弧下标

    bool BFS(){
        mem(vis,0);
        queue<int >Q;
        Q.push(s); d[s] = 0;vis[s] = 1;
        while(!Q.empty()){
            int x = Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge &e = edges[G[x][i]];
                if(!vis[e.to] && e.cap > e.flow){//只考虑残量网络中的弧
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x == t || a == 0) return a;
        int flow = 0,f;
        for(int &i = cur[x];i<G[x].size();i++){
            Edge&e = edges[G[x][i]];
            if(d[x] + 1 ==d[e.to] && (f = DFS(e.to,min(a,e.cap - e.flow))) > 0){
                e.flow += f;
                edges[G[x][i]^1].flow -= f;// 反向弧
                flow += f;
                a-=f;
                if(a == 0) break;
            }
        }
        return flow;
    }
    ll MaxFlow(int s,int t){
        this->s = s;this->t = t;
        ll flow = 0;
        while(BFS()){
            mem(cur,0);
            flow += DFS(s,INF);
        }
        return flow;
    }
    void addEdge(int from,int to,int cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    void init(){
        edges.clear();
        for(int i=0;i<=N;i++){
            G[i].clear();d[i] =0; vis[i] = 0;
        }
        mem(cur,0);
    }
}solve;
int main(){
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        int n,m;scanf("%d%d",&n,&m);
        int u,v,c,d;
        solve.init();
        for(int i=0;i<m;i++){
        scanf("%d%d%d%d",&u,&v,&c,&d);
        solve.addEdge(u,v,c * N + 1);
        if(d)
            solve.addEdge(v,u,c*N+1);
        }
        ll ans = solve.MaxFlow(0,n-1);
        printf("Case %d: %I64d\n",cas,ans%(N));
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值