网络流进阶

先把三道例题扔在这里吧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;
}

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

时间: 2024-10-14 17:33:43

网络流进阶的相关文章

[知识点]网络流进阶之对偶图

一.前言 本文为上一篇文章<网络流基础>之续集,同样3年前已有一篇文章讲解转换对偶图,这里再次为其翻新一次,希望能够更好理解. 二.最小割 讲网络流不得不提一个概念--最小割.便于理解,上一篇文章并没有将其搅和进来.最小割是什么呢?现在要求割断部分路径上的流量,使从源点没有任何流量可以到达汇点,而截取的流量最小值即最小割.我们再次拿出上次的模型: 首先从1至4最直接的20流量必然需要截掉:从1至2理应截取40,但由于2-3-4路径上的最大流仅为10,加上2-4流量为20,故只需截取30:总计5

网络流(进阶)

最大流:DINIC or SAP 最小费用最大流:SPFA+增广(费用的值较离散) or ZKW(费用的值集中) 有源汇的上下界最大流:新建s', t',用(i, j, l, r)表示i到j有一条下界为l上界为r的边,将每条这样的边拆成(s', j, 0, l), (i, t', 0, l), (i, j, 0, r-l),加入边(t, s, 0, max)再从s'到t'求最大流,再去掉(t, s, 0, max)这条边,从s到t求最大流 有源汇的上下界最小可行流:基本同上,将最后一步改成从t到

C++之路进阶——网络流(网络扩容)

1362 网络扩容 省队选拔赛 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用.求: 1.  在不扩容的情况下,1到N的最大流: 2.  将1到N的最大流增加K所需的最小扩容费用. 输入描述 Input Description 输入文件的第一行包含三个整数N,M,K,表示有向图的点数.边数以及所需要增加的流量. 接下来的M行每行包含

ACM进阶计划

转自 http://www.cnblogs.com/zxhl/p/4690748.html ACM进阶计划ACM队不是为了一场比赛而存在的,为的是队员的整体提高.大学期间,ACM队队员必须要学好的课程有:lC/C++两种语言l高等数学l线性代数l数据结构l离散数学l数据库原理l操作系统原理l计算机组成原理l人工智能l编译原理l算法设计与分析除此之外,我希望你们能掌握一些其它的知识,因为知识都是相互联系,触类旁通的.以下学习计划每学期中的内容不分先后顺序,虽说是为立志于学习ACM的同学列的知识清单

[转]ACM进阶计划

ACM进阶计划  大学期间,ACM队队员必须要学好的课程有: lC/C++两种语言 l高等数学 l线性代数 l数据结构 l离散数学 l数据库原理 l操作系统原理 l计算机组成原理 l人工智能 l编译原理 l算法设计与分析 除此之外,我希望你们能掌握一些其它的知识,因为知识都是相互联系,触类旁通的. 以下学习计划每学期中的内容不分先后顺序,虽说是为立志于学习ACM的同学列的知识清单,但内容不限于ACM的知识.英语之类与专业相距较远的课程请自行分配时间,这里不再列举.  大一上学期: 必学: 2.简

ACM进阶

ACM队不是为了一场比赛而存在的,为的是队员的整体提高. 大学期间,ACM队队员必须要学好的课程有: l C/C++两种语言 l 高等数学 l 线性代数 l 数据结构 l 离散数学 l 数据库原理 l 操作系统原理 l 计算机组成原理 l 人工智能 l 编译原理 l 算法设计与分析 除此之外,我希望你们能掌握一些其它的知识,因为知识都是相互联系,触类旁通的. 以下学习计划每学期中的内容不分先后顺序,虽说是为立志于学习ACM的同学列的知识清单,但内容不限于ACM的知识.英语之类与专业相距较远的课程

编程进阶(转载)

ACM算法列表 ACM所有算法 数据结构 栈,队列,链表 哈希表,哈希数组 堆,优先队列双端队列可并堆左偏堆 二叉查找树Treap伸展树 并查集集合计数问题二分图的识别 平衡二叉树 二叉排序树 线段树一维线段树二维线段树 树状数组一维树状数组N维树状数组 字典树 后缀数组,后缀树 块状链表 哈夫曼树 桶,跳跃表 Trie树(静态建树.动态建树) AC自动机 LCA和RMQ问题 KMP算法 图论 基本图算法图广度优先遍历深度优先遍历拓扑排序割边割点强连通分量Tarjan算法双连通分量强连通分支及其

图论进阶题目分类

今天在wind里看到他刷的图论进阶 于是我把他的也摘了过来 尽量刷吧 我只写了题号 HDU 1213 1272 1325&&poj1308 1856 1102 1232 1233 1863 1875 1879 3371 1301 1162 1198 1598 1811 3926 3938 2489 4081 4126 1829&&poj2492 1558 3461 3367 2473 3172 3635 3047 3038 2818 3234 2121 4009 3311

关于网络流算法(2)

了解最大流解法: 网络流的相关基础知识很容易获得,详细的有<算导>,简单的有刘汝佳<算法竞赛入门>,这里选用的也是刘的书从Page207开始的内容. 这里要补充一些值得注意的基础: 最大流问题中的三个约束条件:容量限制条件.斜对成性条件.流量平衡条件: 网络流问题中边(Edge)的有向性. 1.1 BFS算法 因为<算法竞赛入门>中的E-K算法是基于BFS遍历方法的,说是很容易找到让DFS很慢的例子所以改为BFS,自己又已经把以前看的书全部忘记了,无奈只能开这个1.1,