zoukankan      html  css  js  c++  java
  • 酒店之王

    其实我最近一直在搞网络瘤,虽然并没有发博客

    酒店之王

    这道题并不难,

    我们一眼就能看出是一道网络瘤(bushi),

    建模也没有难度,

    就是把每个人和他喜欢的菜连起来,

    再和他喜欢的房子连起来,

    燃鹅,

    出现了一个小问题,

    就是每个人可能在连线的过程中被重复利用。

    其实我最初的解决方案是先见一个超级源,

    再将每个人和超级源之间建一条边,

    将人连接到他喜欢的房子,

    再将他喜欢的房子连接到他喜欢的菜。

    于是乎,

    就有了下面的代码:

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 2e6 + 5;
    int n, p, q, cai[105][105], fang[105][105];
    int tot = 1, to[maxn], cap[maxn], nxt[maxn], head[maxn];
    int s, t, now[maxn], d[maxn], maxflow;
    queue <int> qq;
    void add(int x, int y, int z){
        to[++tot] = y, cap[tot] = z, nxt[tot] = head[x], head[x] = tot;
    }
    bool BFS(){
        memset(d, 0, sizeof(d));
        while (qq.size()) qq.pop();
        qq.push(s); d[s] = 1, now[s] = head[s];
        while (qq.size()){
            int x = qq.front(); qq.pop();
            for (int i = head[x]; i; i = nxt[i]){
                int y = to[i];
                if (!d[y] && cap[i]){
                    qq.push(y);
                    d[y] = d[x] + 1;
                    now[y] = head[y];
                    if (y == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if (x == t || flow == 0) return flow;
        int rest = flow, k;
        int& i = now[x];
        for (; i && rest; i = nxt[i]){
            int y = to[i];
            if (d[y] == d[x] + 1 && cap[i]){
                k = dinic(y, min(rest, cap[i]));
                if (!k) d[y] = 0;
                cap[i] -= k;
                cap[i ^ 1] += k;
                rest -= k;
            }
        }
        now[x] = i;
        return flow - rest;
    }
    int main(){
        scanf("%d%d%d", &n, &p, &q); s = 0, t = n + p + q + 1;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= p; j++)
                scanf("%d", &fang[i][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= q; j++)
                scanf("%d", &cai[i][j]);
        for (int i = 1; i <= n; i++){
            add(s, i, 1); add(i, s, 0);
            for (int j = 1; j <= p; j++)
                if (fang[i][j]){
                    add(i, j + n, 1); add(j + n, i, 0);
                    for (int k = 1; k <=q; k++)
                        if (cai[i][k]){
                            add(j + n, k + n + p, 1); add(k + n + p, j + n, 0);
                            add(k + n + p, t, 1); add(t, k + n + p, 0);
                        }
                }
        }
        int flow = 0;
        while (BFS())
            while (flow = dinic(s, 100)) maxflow += flow;
        printf("%d", maxflow);
        return 0;
    }
    

    这样居然可以拿到(40pts)!!!

    其实这是一个错误的代码,

    毕竟样例都没过(什么!?你问我样例都没过为什么要交?因为我觉得我做对了阿巴阿巴

    尽管思路有可取之处。

    为什么说这是错误的呢?

    因为我们发现,

    如果这样建边的话,

    我们中间的节点(也就是每个房)其实可以用多次,

    因为他没有点权。

    什么!!!

    没有点权???

    那就加上呗!

    bulubula

    赛高!

    新代码!!!

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 2e6 + 5;
    int n, p, q, cai[105][105], fang[105][105];
    int tot = 1, to[maxn], cap[maxn], nxt[maxn], head[maxn];
    int s, t, now[maxn], d[maxn], maxflow;
    queue <int> qq;
    void add(int x, int y, int z){
        to[++tot] = y, cap[tot] = z, nxt[tot] = head[x], head[x] = tot;
    }
    bool BFS(){
        memset(d, 0, sizeof(d));
        while (qq.size()) qq.pop();
        qq.push(s); d[s] = 1, now[s] = head[s];
        while (qq.size()){
            int x = qq.front(); qq.pop();
            for (int i = head[x]; i; i = nxt[i]){
                int y = to[i];
                if (!d[y] && cap[i]){
                    qq.push(y);
                    d[y] = d[x] + 1;
                    now[y] = head[y];
                    if (y == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if (x == t || flow == 0) return flow;
        int rest = flow, k;
        int& i = now[x];
        for (; i && rest; i = nxt[i]){
            int y = to[i];
            if (d[y] == d[x] + 1 && cap[i]){
                k = dinic(y, min(rest, cap[i]));
                if (!k) d[y] = 0;
                cap[i] -= k;
                cap[i ^ 1] += k;
                rest -= k;
            }
        }
        now[x] = i;
        return flow - rest;
    }
    int main(){
        scanf("%d%d%d", &n, &p, &q); s = 0, t = n + p + q + 1;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= p; j++)
                scanf("%d", &fang[i][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= q; j++)
                scanf("%d", &cai[i][j]);
        for (int i = 1; i <= n; i++){
            add(s, i, 1); add(i, s, 0);
            for (int j = 1; j <= p; j++)
                if (fang[i][j]){
                    add(i, j + n, 1); add(j + n, i, 0);
                    for (int k = 1; k <=q; k++)
                        if (cai[i][k]){
                            add(j + t, k + n + p, 1); add(k + n + p, j + t, 0);
                            add(k + t + p, t, 1); add(t, k + t + p, 0);
                        }
                }
        }
        for (int i = 1; i <= p; i++){
            add(n + i, t + i, 1); add(t + i, n + i, 0);
        }
        for (int i = 1; i <= q; i++){
            add(n + p + i, t + p + i, 1); add(t + p + i, n + p + i, 0);
        }
        int flow = 0;
        while (BFS())
            while (flow = dinic(s, 100)) maxflow += flow;
        printf("%d", maxflow);
        return 0;
    }
    

    这里我们把每道菜和房子加上了点权,(拆边

    然后自信提交。

    纳尼!!!

    (70pts)???

    此时此刻……

    我陷入了沉思……

    突然!

    很快啊!

    我想到了一个非常严重的问题,

    我的边可能被重复利用!

    比如我将1号和他喜欢的菜还有房子连边,

    其他人仍然可以用这条边!

    所以这种建边方式也是错的。

    如何避免边也被重复利用呢?

    因为每道菜和房子都会被很多人连边,

    所以我们不能让菜和房子连边,

    (为了避免不必要的牺牲bushi

    所以!!!

    这里我们想到让人和菜还有房子分别连边,

    这样的话,

    我们就让超级源和超级汇分别连向房子和菜,

    然后把人放在中间。

    由于我真的是太激动了,

    直接交了一发。

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e6 + 5;
    int n, p, q, cai[105][105], fang[105][105];
    int tot = 1, to[maxn], cap[maxn], nxt[maxn], head[maxn];
    int s, t, now[maxn], d[maxn], maxflow;
    queue <int> qq;
    void add(int x, int y, int z){
        to[++tot] = y, cap[tot] = z, nxt[tot] = head[x], head[x] = tot;
    }
    bool BFS(){
        memset(d, 0, sizeof(d));
        while (qq.size()) qq.pop();
        qq.push(s); d[s] = 1, now[s] = head[s];
        while (qq.size()){
            int x = qq.front(); qq.pop();
            for (int i = head[x]; i; i = nxt[i]){
                int y = to[i];
                if (!d[y] && cap[i]){
                    qq.push(y);
                    d[y] = d[x] + 1;
                    now[y] = head[y];
                    if (y == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if (x == t || flow == 0) return flow;
        int rest = flow, k;
        int& i = now[x];
        for (; i && rest; i = nxt[i]){
            int y = to[i];
            if (d[y] == d[x] + 1 && cap[i]){
                k = dinic(y, min(rest, cap[i]));
                if (!k) d[y] = 0;
                cap[i] -= k;
                cap[i ^ 1] += k;
                rest -= k;
            }
        }
        now[x] = i;
        return flow - rest;
    }
    int main(){
        scanf("%d%d%d", &n, &p, &q); s = 0, t = 501;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= p; j++)
                scanf("%d", &fang[i][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= q; j++)
                scanf("%d", &cai[i][j]);
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= p; j++)
                if (fang[i][j]){
                    add(j + 100, i, 1); add(i, j + 100, 0);
                }
            for (int j = 1; j <= q; j++)
                if (cai[i][j]){
                    add(i, j + 200, 1); add(j + 200, i, 0);
                }
        }
        for (int i = 1; i <= p; i++){
            add(s, i + 100, 1); add(i + 100, s, 0);
        }
        for (int i = 1; i <= q; i++){
            add(i + 200, t, 1); add(t, i + 200, 0);
        }
        int flow = 0;
        while (BFS())
            while (flow = dinic(s, 100)) maxflow += flow;
        printf("%d", maxflow);
        return 0;
    }
    

    然后嘞?

    (60pts)!!!(居然还少了(10pts)?!

    为什么呢?

    因为一个人只能用一次,

    所以每个人相当于有(1)的点权,

    然后我们把每个人拆开,

    将拆出来的两个人连边,

    边权为(1)

    再跑一遍(dinic)

    这样就解决了。

    代码:

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e5 + 5;
    int n, p, q, cai[105][105], fang[105][105];
    int tot = 1, to[maxn], cap[maxn], nxt[maxn], head[maxn];
    int s, t, now[maxn], d[maxn], maxflow;
    queue <int> qq;
    void add(int x, int y, int z){
        to[++tot] = y, cap[tot] = z, nxt[tot] = head[x], head[x] = tot;
    }
    bool BFS(){
        memset(d, 0, sizeof(d));
        while (qq.size()) qq.pop();
        qq.push(s); d[s] = 1, now[s] = head[s];
        while (qq.size()){
            int x = qq.front(); qq.pop();
            for (int i = head[x]; i; i = nxt[i]){
                int y = to[i];
                if (!d[y] && cap[i]){
                    qq.push(y);
                    d[y] = d[x] + 1;
                    now[y] = head[y];
                    if (y == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if (x == t || flow == 0) return flow;
        int rest = flow, k;
        int& i = now[x];
        for (; i && rest; i = nxt[i]){
            int y = to[i];
            if (d[y] == d[x] + 1 && cap[i]){
                k = dinic(y, min(rest, cap[i]));
                if (!k) d[y] = 0;
                cap[i] -= k;
                cap[i ^ 1] += k;
                rest -= k;
            }
        }
        now[x] = i;
        return flow - rest;
    }
    int main(){
        scanf("%d%d%d", &n, &p, &q); s = 0, t = 401;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= p; j++)
                scanf("%d", &fang[i][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= q; j++)
                scanf("%d", &cai[i][j]);
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= p; j++)
                if (fang[i][j]) add(j + 200, i, 1), add(i, j + 200, 0);
            for (int j = 1; j <= q; j++)
                if (cai[i][j]) add(i + 100, j + 300, 1), add(j + 300, i + 100, 0);
        }
        for (int i = 1; i <= n; i++) add(i, i + 100, 1), add(i + 100, i, 0);
        for (int i = 1; i <= p; i++) add(s, i + 200, 1), add(i + 200, s, 0);
        for (int i = 1; i <= q; i++) add(i + 300, t, 1), add(t, i + 300, 0);
        int flow = 0;
        while (BFS())
            while (flow = dinic(s, 100)) maxflow += flow;
        printf("%d", maxflow);
        return 0;
    }
    

    是不是!

    是不是经整理之后的代码好看多了!!!

    哈哈哈哈哈!!!(憨批

    看不见我看不见我看不见我
  • 相关阅读:
    C# 多态类型
    字符集(Character set)
    CSS之backgroundposition 百分数形式
    javascript this对象 作用域链scope chain
    地图 查经纬度 算距离
    JavaScript 对象封装(完美版)
    iTextSharp 使用详解(转)
    社交网站 分享 +button
    COCOS2DX学习笔记(一) windows下cocos2dx的环境配置
    【首创】完美解决scrollview与menu的兼容问题
  • 原文地址:https://www.cnblogs.com/sxy2004/p/14585665.html
Copyright © 2011-2022 走看看