酒店之王

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

酒店之王

这道题并不难,

我们一眼就能看出是一道网络瘤(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;
}

是不是!

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

哈哈哈哈哈!!!(憨批

看不见我看不见我看不见我
原文地址:https://www.cnblogs.com/sxy2004/p/14585665.html