[網絡流 24 題] luoguP2763 試題庫問題

[返回網絡流 24 題索引]

題目描述

假設一個試題庫中有 nn 道試題。每道試題都標明瞭所屬類別。同一道題可能有多個類別屬性。現要從題庫中抽取 mm 道題組成試卷。並要求試卷包含指定類型的試題。

Solution 2763\text{Solution 2763}

設表示題目的點爲 PP,表示類別的爲 KK
首先從源 STSTPiP_i 連一條流量爲 11 的邊,即每道題只能選一次;
然後從 KiK_iEDED 連一條流量爲 aia_i 的邊,即每種類型需要選 aia_i 道;

最後 jTi\forall j \in T_i,從 PiP_iKjK_j 連邊,即每道題可以以相應類型的名義被選出。

方案即是所有 題目到類型 的連邊中滿流的那些。如果流量不滿則無解。

附上 dalao ⚡cdecl⚡ 的代碼。

#include <cstdio>
#include <cstring>
#include <queue>

const int N = 1e3 + 31, M = 3e4 + 43, K = 22, INF = 0x3f3f3f3f;

struct edge {
    int to, next, w;
} e[M << 1];
int head[N], cnt = 1;
void addedge(int x, int y, int z) {
    e[++cnt] = (edge){y, head[x], z};
    head[x] = cnt;
    e[++cnt] = (edge){x, head[y], 0};
    head[y] = cnt;
}

int level[N];
bool bfs(int s, int t) {
    memset(level, 0, sizeof level);
    std::queue<int> q;
    q.push(s);
    level[s] = 1;
    while (!q.empty()) {
        int pos = q.front();
        q.pop();
        for (int i = head[pos]; i; i = e[i].next) {
            int nx = e[i].to;
            if (level[nx] || !e[i].w) continue;
            level[nx] = level[pos] + 1;
            q.push(nx);
        }
    }
    return level[t];
}

int dfs(int s, int t, int flow) {
    if (s == t) return flow;
    int ret = 0;
    for (int i = head[s]; flow && i; i = e[i].next) {
        int nx = e[i].to;
        if (level[s] + 1 == level[nx] && e[i].w) {
            int tmp = dfs(nx, t, std::min(flow, e[i].w));
            e[i].w -= tmp;
            e[i ^ 1].w += tmp;
            ret += tmp;
            flow -= tmp;
        }
    }
    return ret;
}

int dinic(int s, int t) {
    int ret = 0;
    while (bfs(s, t)) ret += dfs(s, t, INF);
    return ret;
}

std::queue<int> out[K];
int n, k, x, y, sum;
int main() {
    scanf("%d%d", &k, &n);
    for (int i = 1; i <= k; i++) {
        scanf("%d", &x);
        addedge(n + i + 1, n + k + 2, x);
        sum += x;
    }
    for (int i = 1; i <= n; i++) {
        addedge(1, i + 1, 1);
        for (scanf("%d", &x); x--;) {
            scanf("%d", &y);
            addedge(i + 1, n + y + 1, 1);
        }
    }
    if (dinic(1, n + k + 2) != sum) return puts("No Solution!"), 0;
    for (int i = 1; i <= n; i++) {
        for (int j = head[i + 1]; j; j = e[j].next) {
            if ((~j & 1) && !e[j].w) {
                out[e[j].to - n - 1].push(i);
                break;
            }
        }
    }
    for (int i = 1; i <= k; i++) {
        printf("%d: ", i);
        if (out[i].empty()) puts("");
        while (!out[i].empty()) {
            printf("%d%c", out[i].front(), " \n"[out[i].size() == 1]);
            out[i].pop();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章