码迷,mamicode.com
首页 > 其他好文 > 详细

网络流进阶

时间:2019-03-23 01:02:01      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:res   网络   algorithm   min   bfs   empty   math   最大   就是   

先把三道例题扔在这里吧qvq
无源汇有上下界可行流
有源汇有上下界最大流
有源汇有上下界最小流

无源汇有上下界可行流

首先默认把下界跑满 但显然这样是不满足流量平衡的
所以建立超级源S,超级汇T
把所有“入不敷出”的点向T连一条大小为 出流-入流 的边
把S向所有入流大的点连一条大小为 入流-出流 的边
此时跑一遍网络流 如果与S相连的边都满流了 那么T也满流了
整个图即可行 否则不可行

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d\n", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, cnt, mf;
int low[M], upp[M], in[N], out[N];
int dis[N], fro;
queue<int> que;
bool bfs(){
    memset(dis, 0, sizeof(dis));
    que.push(S); dis[S] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    return dis[T];
}
int dfs(int x, int rest){
    if(!rest || x == T) return rest;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest, edge[i].f));
            if(d > 0){
                edge[i].f -= d;
                edge[i ^ 1].f += d;
                return d; 
            }
        }
    }
    return 0;
}

void dinic(){
    mf = 0;
    while(bfs()){
        //for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]); 
        for(int i = 1; i <= T; ++i) cur[i] = head[i];
        mf += dfs(S, inf);
        //printf("mf = %d\n", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        out[x] += low[i];
        in[y] += low[i];
    }
    S = n + 1, T = n + 2;
    for(int i = 1; i <= n; ++i){
        if(in[i] > out[i]){
            addedge(S, i, in[i] - out[i]);
            cnt += in[i] - out[i]; 
        }
        else if(out[i] > in[i]) addedge(i, T, out[i] - in[i]);
    }
    dinic();
    if(mf == cnt){
        printf("YES\n");
        for(int i = 1; i <= m; ++i){
            printf("%d\n", low[i] + edge[(i << 1) - 1].f);
        }
    } 
    else printf("NO\n");
    return 0;   
}

有源汇有上下界最大流

在原图上再跑一次最大流就好啦

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d\n", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
    memset(dis, 0, sizeof(dis));
    que.push(s); dis[s] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    //printf("%d\n", dis[T]);
    return dis[t];
}
int dfs(int x, int rest, int t){
    //  printf("%d %d\n", x, rest);
    if(!rest || x == t) return rest;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest, edge[i].f), t);
            if(d > 0){
                //printf("%d\n", d);
                edge[i].f -= d;
                edge[i ^ 1].f += d;
                return d; 
            }
        }
    }
    return 0;
}

void dinic(int s, int t){
    mf = 0;
    while(bfs(s, t)){
        //for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]); 
        memcpy(cur, head, sizeof(cur));
        mf += dfs(s, inf, t);
        //printf("mf = %d\n", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d%d%d", &n, &m, &S, &T);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        d[x] -= low[i];
        d[y] += low[i];
    }
    //printf("*");
    SS = n + 1, TT = n + 2;
    for(int i = 1; i <= n; ++i){
        if(d[i] > 0){
            addedge(SS, i, d[i]);
            cnt += d[i]; 
        }
        else if(d[i] < 0) addedge(i, TT, -d[i]);
    }
    addedge(T, S, inf);
    int rec = esize;
    
    dinic(SS, TT);
    
    if(mf != cnt){
        printf("please go home to sleep\n");
        return 0;
    } 
    
    head[S] = edge[head[S]].next;
    head[T] = edge[head[T]].next;
    dinic(S, T); mf += edge[rec].f;
    printf("%d", mf);
    return 0;   
}

有源汇有上下界最小流

1.从SS到TT跑最大流
2.从T到S连一条大小为INF的边
3.从SS到TT跑最大流
4.最后T到S的流量就是最小流

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 5e4 + 9;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d\n", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
    memset(dis, 0, sizeof(dis));
    que.push(s); dis[s] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    //printf("%d\n", dis[T]);
    return dis[t];
}
int dfs(int x, int rest, int t){
    //  printf("%d %d\n", x, rest);
    if(!rest || x == t) return rest;
    int tmp = 0;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest - tmp, edge[i].f), t);
            edge[i].f -= d;
            edge[i ^ 1].f += d;
            tmp += d;
            if(tmp == rest) return tmp;
        }
    }
    return tmp;
}

void dinic(int s, int t){
    while(bfs(s, t)){
        //for(int i = 1; i <= T; ++i) printf("%d %d\n", i, dis[i]); 
        memcpy(cur, head, sizeof(cur));
        mf += dfs(s, inf, t);
        //printf("mf = %d\n", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d%d%d", &n, &m, &S, &T);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        d[x] -= low[i];
        d[y] += low[i];
    }
    //printf("*");
    SS = n + 1, TT = n + 2;
    for(int i = 1; i <= n; ++i){
        if(d[i] > 0){
            addedge(SS, i, d[i]);
            cnt += d[i]; 
        }
        else if(d[i] < 0) addedge(i, TT, -d[i]);
    }
    
    dinic(SS, TT);
    
    addedge(T, S, inf);
    int rec = esize;
    
    dinic(SS, TT); 
    if(mf != cnt){
        printf("please go home to sleep\n");
        return 0;
    } 
    else printf("%d", edge[rec].f);
    return 0;   
}

网络流进阶

标签:res   网络   algorithm   min   bfs   empty   math   最大   就是   

原文地址:https://www.cnblogs.com/hjmmm/p/10581141.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!