P2763 试题库问题 最大流

https://www.luogu.org/problem/P2763

题意:很简单,都是中文,给出每种类型试题的数量,同时,给出每道题,可以充当的试题种类。

做法:设一个源点,与每一个试题类型建边,容量为试题的数量,同时每种试题类型在对每一个题型建立一条边,容量为1,最后在连接汇点。跑一个最大流即可。

#include "bits/stdc++.h"

using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
const int inf = 0x3f3f3f3f;
const int maxn = 100000 + 10;
int head[maxn], dis[maxn], cnt;
struct node {
    int val, v, nxt;
} e[maxn << 1];
void init() {
    cnt = 0;
    memset(head, -1, sizeof(head));
}
void add_edge(int u, int v, int val) {
    e[cnt].v = v;
    e[cnt].val = val;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}
int bfs(int s, int t) {
    queue<int> qu;
    memset(dis, -1, sizeof(dis));
    dis[s] = 0;
    qu.push(s);
    while (!qu.empty()) {
        int x = qu.front();
        qu.pop();
        for (int i = head[x]; i != -1; i = e[i].nxt) {
            int now = e[i].v;
            if (dis[now] == -1 && e[i].val != 0) {
                dis[now] = dis[x] + 1;
                qu.push(now);
            }
        }
    }
    return dis[t] != -1;
}
int dfs(int x, int t, int maxflow) {
    if (x == t) return maxflow;
    int ans = 0;
    for (int i = head[x]; i != -1; i = e[i].nxt) {
        int now = e[i].v;
        if (dis[now] != dis[x] + 1 || e[i].val == 0 || ans >= maxflow)
            continue;
        int f = dfs(now, t, min(e[i].val, maxflow - ans));
        e[i].val -= f;
        e[i ^ 1].val += f;
        ans += f;
    }
    return ans;
}
int Dinic(int s, int t) {
    int ans = 0;
    while (bfs(s, t)) {
        ans += dfs(s, t, inf);
    }
    return ans;
}
set<int> se[maxn];
int a[maxn];
int main() {
    init();
    int k, n, ss, tt, x, cc, sum = 0;
    scanf("%d%d", &k, &n);
    ss = 0;
    for (int i = 1; i <= k; i++) {
        scanf("%d", &x);
        sum += x;
        add_edge(ss, i, x);
        add_edge(i, ss, 0);
    }
    tt = n + k + 1;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &cc);
        while (cc--) {
            scanf("%d", &x);
            add_edge(x, i + k, 1);
            add_edge(i + k, x, 0);
        }
        add_edge(i + k, tt, 1);
        add_edge(tt, i + k, 0);
    }
    int ans = Dinic(ss, tt);
    if (ans != sum) {
        printf("No Solution!\n");
    } else {
        for (int i = 0; i < cnt; i += 2) {
            if (e[i].v != ss && e[i ^ 1].v != ss)
                if (e[i].v != tt && e[i ^ 1].v != tt)
                    if (e[i ^ 1].val != 0) {
                        se[e[i ^ 1].v].insert(e[i].v - k);
                    }

        }
        for (int i = 1; i <= k; i++) {
            printf("%d:", i);
            for (auto it = se[i].begin(); it != se[i].end(); it++) {
                printf(" %d", *it);
            }
            cout << endl;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值