最大流总结

Posted 我不吃饼干呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大流总结相关的知识,希望对你有一定的参考价值。

准备开始学习最大流。模板是在网上抄的,感觉这个看起来比较优雅。http://blog.csdn.net/d891320478/article/details/8424820

持续更新。

(hdu zoj poj vj 都挂了 还怎么刷题啊……)

(2016.9.6

sgu326. Perspective 

题意:一个小组有n个队伍1~n,每场比赛一个队伍赢,一个队伍输,现在已知每个队伍i都已经赢了Wi场比赛,同时知道每个队伍还需要打Ri场比赛,包括组内和组外,同时知道组内还剩下的比赛场数,Aij表示i和j还需要打几场比赛,Aij=Aji,∑(j:1~n)Aij <= Ri

问题是队伍1能否成为小组获胜场数最多的,可以并列第一。

>>首先让队伍1赢得所有的比赛,其他队伍输掉所有组外的比赛。然后建图最大流。

/*
 * 建一个源点 然后对于每一场比赛为一个点 从源点到比赛连边,权值为比赛的总分数
 * 比赛这个点和比赛的两个队伍分别连边 权值大于等于比赛最大分数即可
 * 每个队伍到汇点连一条边 权值应为该队伍和1队分数的差值(如果分数大于1队,应该直接输出no
 * 走一遍最大流 如果答案是所有比赛的分数和就可以满足要求
 */
#include <bits/stdc++.h>

const int N = 1000;
const int M = 1000000;
const int INF = 1 << 30;
struct Edge {
    int to, next, w;
} edge[M];
int head[N], cntE;
int src, sink;
int pre[N], cur[N], dis[N], gap[N];
int que[N], open, tail;
void addedge(int u, int v, int w) {
    edge[cntE].to = v;
    edge[cntE].w = w;
    edge[cntE].next = head[u];
    head[u] = cntE++;
    edge[cntE].to = u;
    edge[cntE].w = 0;
    edge[cntE].next = head[v];
    head[v] = cntE++;
}
void BFS() {
    int i, u, v;
    memset(gap, 0, sizeof(gap));
    memset(dis, -1, sizeof(dis));
    open = tail = 0;
    que[open] = sink;
    dis[sink] = 0;
    while (open <= tail) {
        u = que[open++];
        for (i = head[u]; i != -1; i = edge[i].next) {
            v = edge[i].to;
            if (edge[i].w != 0 || dis[v] != -1) continue;
            que[++tail] = v;
            ++gap[dis[v] = dis[u] + 1];
        }
    }
}
int sap(int n) { //编号从1开始 1~n
    int i, v, u, flow = 0, aug = INF;
    int flag;
    BFS();
    gap[0] = 1;
    for (i = 1; i <= n; i++) cur[i] = head[i];
    u = pre[src] = src;
    while (dis[src] < n) {
        flag = 0;
        for (int j = cur[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && dis[u] == dis[v] + 1) {
                flag = 1;
                if (edge[j].w < aug) aug = edge[j].w;
                pre[v] = u;
                u = v;
                if (u == sink) {
                    flow += aug;
                    while (u != src) {
                        u = pre[u];
                        edge[cur[u]].w -= aug;
                        edge[cur[u] ^ 1].w += aug;
                    }
                    aug = INF;
                }
                break;
            }
            cur[u] = edge[j].next;
        }
        if (flag) continue;
        int mindis = n;
        for (int j = head[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && mindis > dis[v]) {
                mindis = dis[v];
                cur[u] = j;
            }
        }
        if (--gap[dis[u]] == 0) break;
        ++gap[dis[u] = mindis + 1];
        u = pre[u];
    }
    return flow;
}

int w[N], r[N], a[N][N];
int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; ++i) scanf("%d", w+i);//每个队伍已经赢了多少场比赛
        for (int i = 1; i <= n; ++i) scanf("%d", r+i);//每个队伍还可以打多少场比赛,包括本组和外组
        for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);//本组剩余比赛情况
        w[1] += r[1]; //贪心 让本队全赢
        bool fg = false;
        for (int i = 2; i <= n; ++i) if (w[i] > w[1]) { fg = true; break; }
        if (fg) { puts("NO"); continue; }
        src = 1;
        int cnt = n+1;
        int sum = 0;
        cntE = 0;
        memset(head, -1, sizeof head);
        for (int i = 2; i <= n; ++i) for (int j = i; j <= n; ++j) {
            if (a[i][j] <= 0) continue;
            sum += a[i][j];
            addedge(src, cnt, a[i][j]);
            addedge(cnt, i, INF);
            addedge(cnt, j, INF);
            cnt++;
        }
        sink = cnt;
        for (int i = 2; i <= n; ++i) addedge(i, sink, w[1]-w[i]);
        if (sap(sink) == sum) puts("YES");
        else puts("NO");
    }
    return 0;
}
View Code

 (2016.9.7

codeforces546E-Soldier and Traveling

题意:有n个城市,m条无向图,每个城市有ai的士兵,每个士兵可以走到相邻的城市(只能走一步)或者原地不动。问能不能使得每个城市的士兵变成bi。

题解:首先有一个trick,就是sigma(ai)!=sigma(bi)的时候,记得输出NO。

>>这题需要输出点与点的转移数量,在求最大流的过程中记录

/* 把每个点拆成入点和出点
 * 从源点到每一个入点连一条 流量为该点的初始人数
 * 从每个点的入点到出点连一条边 流量为该点的初始人数
 * 对于每条边 从起点的入点到终点的出点连一条边 流量为起点的初始人数
 * 每个出点连汇点 流量为该点的结束人数
 */
#include <bits/stdc++.h>

const int N = 1000;
const int M = 1000000;
const int INF = 1 << 30;
struct Edge {
    int from, to, next, w;
} edge[M];
int head[N], cntE;
int src, sink;
int pre[N], cur[N], dis[N], gap[N];
int que[N], open, tail;

int ans[N][N];

void addedge(int u, int v, int w) {
    edge[cntE].from = u;
    edge[cntE].to = v;
    edge[cntE].w = w;
    edge[cntE].next = head[u];
    head[u] = cntE++;
    edge[cntE].from = v;
    edge[cntE].to = u;
    edge[cntE].w = 0;
    edge[cntE].next = head[v];
    head[v] = cntE++;
}
void BFS() {
    int i, u, v;
    memset(gap, 0, sizeof(gap));
    memset(dis, -1, sizeof(dis));
    open = tail = 0;
    que[open] = sink;
    dis[sink] = 0;
    while (open <= tail) {
        u = que[open++];
        for (i = head[u]; i != -1; i = edge[i].next) {
            v = edge[i].to;
            if (edge[i].w != 0 || dis[v] != -1) continue;
            que[++tail] = v;
            ++gap[dis[v] = dis[u] + 1];
        }
    }
}
int sap(int n) { //编号从1开始 1~n
    int i, v, u, flow = 0, aug = INF;
    int flag;
    BFS();
    gap[0] = 1;
    for (i = 1; i <= n; i++) cur[i] = head[i];
    u = pre[src] = src;
    while (dis[src] < n) {
        flag = 0;
        for (int j = cur[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && dis[u] == dis[v] + 1) {
                flag = 1;
                if (edge[j].w < aug) aug = edge[j].w;
                pre[v] = u;
                u = v;
                if (u == sink) {
                    flow += aug;
                    while (u != src) {
                        u = pre[u];
                        edge[cur[u]].w -= aug;
                        ans[edge[cur[u]].from][edge[cur[u]].to] += aug;
                        edge[cur[u] ^ 1].w += aug;
                        ans[edge[cur[u] ^ 1].from][edge[cur[u] ^ 1].to] -= aug;
                    }
                    aug = INF;
                }
                break;
            }
            cur[u] = edge[j].next;
        }
        if (flag) continue;
        int mindis = n;
        for (int j = head[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && mindis > dis[v]) {
                mindis = dis[v];
                cur[u] = j;
            }
        }
        if (--gap[dis[u]] == 0) break;
        ++gap[dis[u] = mindis + 1];
        u = pre[u];
    }
    return flow;
}

int a[N], b[N];
int main() {
    int n, m, p, q;
    while (~scanf("%d%d", &n, &m)) {
        cntE = 0;
        memset(head, -1, sizeof head);
        memset(ans, 0, sizeof ans);
        src = 2 * n + 1, sink = 2 * n + 2;
        int sum = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", a+i); sum += a[i];
            addedge(src, i, a[i]);
            addedge(i, i+n, a[i]);
        }
        int sum2 = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", b+i); sum2 += b[i];
            addedge(i+n, sink, b[i]);
        }

        for (int i = 1; i <= m; ++i) {
            scanf("%d%d", &p, &q);
            addedge(p, q+n, a[p]);
            addedge(q, p+n, a[q]);
        }
        if (sum != sum2) {
            puts("NO");
            continue;
        }
        if (sap(sink) != sum) {
            puts("NO");
        } else {
            puts("YES");
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= n; ++j) {
                    printf("%d%c", ans[i][j+n], j==n?\'\\n\':\' \');
                }
            }
        }
    }
    return 0;
}
View Code

codeforces653D-Delivery Bears

题意:有n个点m条有向边,每个边有一个容量,从这个边走过的总重量不能超过这个容量,无重边和自环。现在有x只小熊,需要所有的熊从1出发走到n点,每只熊携带相同质量的货物,问最多能运多少货物。

>>开始还以为容量需要小数,后来发现想多了。。。精度什么的,1e-8wa,1e-10超时,改成循环100遍竟然就对了。。。。为什么这么神奇呢。。。。

/* 首先二分每只熊所带的货物重量
 * 对于每条路径,用路径容量除以每只熊的重量就是这条路所能通过熊的数量
 * 因为熊携带的重量会很小,容量又很大,所以可能爆int。。注意下。。
 * 要从源点到1建一个容量为x的边
 * 当最大流为x的时候 说明所有熊都可以走到终点
 */
#include <bits/stdc++.h>
const int N = 1000;
const int M = 1000000;
const int INF = 1 << 30;
struct Edge {
    int to, next, w;
} edge[M];
int head[N], cntE;
int src, sink;
int pre[N], cur[N], dis[N], gap[N];
int que[N], open, tail;

void addedge(int u, int v, int w) {
    edge[cntE].to = v;
    edge[cntE].w = w;
    edge[cntE].next = head[u];
    head[u] = cntE++;
    edge[cntE].to = u;
    edge[cntE].w = 0;
    edge[cntE].next = head[v];
    head[v] = cntE++;
}
void BFS() {
    int i, u, v;
    memset(gap, 0, sizeof(gap));
    memset(dis, -1, sizeof(dis));
    open = tail = 0;
    que[open] = sink;
    dis[sink] = 0;
    while (open <= tail) {
        u = que[open++];
        for (i = head[u]; i != -1; i = edge[i].next) {
            v = edge[i].to;
            if (edge[i].w != 0 || dis[v] != -1) continue;
            que[++tail] = v;
            ++gap[dis[v] = dis[u] + 1];
        }
    }
}
int sap(int n) { //编号从1开始 1~n
    int i, v, u, flow = 0, aug = INF;
    int flag;
    BFS();
    gap[0] = 1;
    for (i = 1; i <= n; i++) cur[i] = head[i];
    u = pre[src] = src;
    while (dis[src] < n) {
        flag = 0;
        for (int j = cur[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && dis[u] == dis[v] + 1) {
                flag = 1;
                if (edge[j].w < aug) aug = edge[j].w;
                pre[v] = u; u = v;
                if (u == sink) {
                    flow += aug;
                    while (u != src) {
                        u = pre[u];
                        edge[cur[u]].w -= aug;
                        edge[cur[u] ^ 1].w += aug;
                    }
                    aug = INF;
                }
                break;
            }
            cur[u] = edge[j].next;
        }
        if (flag) continue;
        int mindis = n;
        for (int j = head[u]; j != -1; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && mindis > dis[v]) {
                mindis = dis[v];
                cur[u] = j;
            }
        }
        if (--gap[dis[u]] == 0) break;
        ++gap[dis[u] = mindis + 1];
        u = pre[u];
    }
    return flow;
}

int a[N], b[N], c[N];
int main() {
    int n, m, x;
    scanf("%d%d%d", &n, &m, &x);
    double l = 0, r = 0;
    for (int i = 0; i < m; ++i) {
        scanf("%d%d%d", a+i, b+i, c+i);
        r += c[i];
    }
    src = n+1, sink = n;
    int cnt= 100;
    while (cnt--) {
        double mid = (l+r) / 2;
        memset(head, -1, sizeof head);
        cntE = 0;
        for (int i = 0; i < m; ++i) {
            int cap = std::min(floor(c[i]/mid), 1e8);
            addedge(a[i], b[i], cap);
        }
        addedge(src, 1, x);
        int tmp = sap(n+1);
        if (tmp == x) l = mid;
        else r = mid;
    }
    printf("%.10f\\n", l*x);
}
View Code

 

codeforces510E-Fox And Dinner

题意:有n个狐狸1~n,每个狐狸ai岁。现在需要将狐狸围桌,一个桌子的狐狸数目要大于等于3,同时相邻的两只狐狸年龄加起来需要是一个质数。问分桌方式,不能分就输出不可能。

>>开始错的很离谱,发现判断质数写错了。。。后来又错的很奇怪,发现sap写错了。。。。

感觉这题比较难,根据奇偶构图第一次见到,学习了。。

/* 首先容易对于两只狐狸,如果年龄和为质数,连边
 * 因为质数都是奇数(ai>=2)可知相邻的狐狸年龄一定是一奇一偶
 * 那么一个桌子的奇数狐狸=偶数狐狸>=2
 * 从源点到每一个偶数建一个流量为2的边
 * 每一个奇数到汇点建一个流量为2的边
 * 然后年龄和为质数的两只狐狸之间建一条流量为1的边
 * 然后跑最大流 如果流量为n就可以
 * 输出比较麻烦。。看完建图题解。。这里还想了半天Orz
 */

#include <bits/stdc++.h>
const int N = 1000;
const int M = 1000000;
const int INF = 1 << 30;
struct Edge {
    int from, to, next, w;
} edge[M];
int head[N], cntE;
int src, sink;
int pre[N], cur[N], dis[N], gap[N];
int que[N], open, tail;
int ans[N][N];
void addedge(int u, int v, int w) {
    edge[cntE].from = u;
    edge[cntE].to = v;
    edge[cntE].w = w;
    edge[cntE].next = head[u];
    head[u] = cntE++;
    edge[cntE].from = v;
    edge[cntE].to = u;
    edge[cntE].w = 0;
    edge[cntE].next = head[v];
    head[v] = cntE++;
}
void BFS() {
    int i, u, v;
    memset(gap, 0, sizeof(gap));
    memset(dis, -1, sizeof(dis));
    open = tail = 0;
    que[open] = sink;
    dis[sink] = 0;
    while (open <= tail) {
        u = que[open++];
        for (i = head[u]; ~i; i = edge[i].next) {
            v = edge[i].to;
            if (edge[i].w != 0 || dis[v] != -1) continue;
            que[++tail] = v;
            ++gap[dis[v] = dis[u] + 1];
        }
    }
}
int sap(int n) { //编号从1开始 1~n
    int i, v, u, flow = 0, aug = INF;
    int flag;
    BFS();
    gap[0] = 1;
    for (i = 1; i <= n; i++) cur[i] = head[i];
    u = pre[src] = src;
    while (dis[src] < n) {
        flag = 0;
        for (int j = cur[u]; ~j; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && dis[u] == dis[v] + 1) {
                flag = 1;
                if (edge[j].w < aug) aug = edge[j].w;
                pre[v] = u; u = v;
                if (u == sink) {
                    flow += aug;
                    while (u != src) {
                        u = pre[u];
                        edge[cur[u]].w -= aug;
                        edge[cur[u] ^ 1].w += aug;
                        ans[edge[cur[u]].from][edge[cur[u]].to] += aug;
                        ans[edge[cur[u]^1].from][edge[cur[u]^1].to] -= aug;
                    }
                    aug = INF;
                }
                break;
            }
            cur[u] = edge[j].next;
        }
        if (flag) continue;
        int mindis = n;
        for (int j = head[u]; ~j; j = edge[j].next) {
            v = edge[j].to;
            if (edge[j].w > 0 && mindis > dis[v]) {
                mindis = dis[v];
                cur[u] = j;
            }
        }
        if (--gap[dis[u]] == 0) break;
        ++gap[dis[u] = mindis + 1];
        u = pre[u];
    }
    return flow;
}

bool isprime(int x) {
    int limit = sqrt(x);
    for (int i = 2; i <= limit; ++i) if (x % i == 0) return false;
    return true;
}

int a[N];
int vis[N];
std::vector<int> G[N];
std::vector<int> ret[N];

void dfs(int x, int u) {
    vis[u] = 1;
    ret[x].push_back(u);
    for (unsigned i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if (!vis[v]) {  dfs(x, v);  break;}
    }
}

int main()
{
    memset(head, -1, sizeof head);
    cntE = 0;
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", a+i);
    }
    src = n+1, sink = n+2;
    int odd = 0, even = 0;;
    for (int i = 1; i <= n; ++i)
        if (a[i] & 1) addedge(i, sink, 2), ++odd;
        else addedge(src, i, 2), ++even;
    if (odd != even) {
        puts("Impossible");
        return 0;
    }
    for (int i = 1; i <= n; ++i)
        for (int j = i+1; j <= n; ++j)
            if (isprime(a[i]+a[j]))
                {
                    if (a[i] & 1) addedge(j, i, 1);
                    else addedge(i, j, 1);
                }
    if (sap(sink) != n) {
        puts("Impossible");
    } else {
        for (int i = 1; i <= n; ++i) {
            网络流24题-题目总结

网络流常见建图套路总结(重制版)

最大流总结

网络流总结

「总结」网络流

图论网络流总结