# [SMOJ2208]试题库问题

• 将试题作为左边的点，从源点向左边每个点连一条容量为 1 的边。（在最终的试卷中，每题充当一个类别的题目）
• 从左边的点向右边的点中，它可以属于的题目类别连一条容量为 1 的边。（同理）
• 将类别作为右边的点，从右边每个点向汇点连一条边，容量为该类别对应需要的题目数量。（注意在本题中对各类别题目的数量有严格限制，容量可以保证上限，即不会多。怎么保证不会少？）

//prog87试题库问题
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

const int MAXK = 20 + 10;
const int MAXN = 1e3 + 10;
const int INF = 0x3f3f3f3f;

struct Edge {
Edge *next;
int cap;
int dest;
} edges[MAXK * MAXN], *current, *first_edge[MAXK + MAXN];

int n, k, s, t;
int need[MAXK];
bool vis[MAXK + MAXN];

Edge *counterpart(Edge *x) {
return edges + ((x - edges) ^ 1);
}

void insert(int u, int v, int c) {
current -> next = first_edge[u];
current -> cap = c;
current -> dest = v;
first_edge[u] = current ++;
}

int dfs(int u, int f) {
//  printf("%d %d\n", u, f);
if (u == t) return f;
if (vis[u]) return 0; else vis[u] = true;
for (Edge *p = first_edge[u]; p; p = p -> next)
if (p -> cap)
if (int res = dfs(p -> dest, min(f, p -> cap))) {
p -> cap -= res;
counterpart(p) -> cap += res;
return res;
}
return 0;
}

int main(void) {
freopen("2208.in", "r", stdin);
freopen("2208.out", "w", stdout);
scanf("%d%d", &k, &n); current = edges;
s = 0; t = n + k + 1;
fill(first_edge, first_edge + t + 1, (Edge*)0);
int m = 0;
for (int i = 1; i <= k; i++) {
scanf("%d", &need[i]);
insert(i, t, need[i]); insert(t, i, 0);
}
for (int i = k + 1; i <= k + n; i++) {
int p; scanf("%d", &p);
insert(s, i, 1); insert(i, s, 0);
while (p--) {
int typ; scanf("%d", &typ);
insert(i, typ, 1); insert(typ, i, 0);
}
}

int ans = 0;
while (true) {
memset(vis, false, sizeof vis);
if (int res = dfs(s, INF)) ans += res; else break;
}
//  printf("%d\n", ans);
if (ans < m) puts("No Solution!");
else {
for (int i = 1; i <= k; i++) {
int c = 0;
for (Edge *p = first_edge[i]; p; p = p -> next) c += (!counterpart(p) -> cap && p -> dest != t);
if (c < need[i]) { puts("No Solution!"); return 0; }
}
for (int i = 1; i <= k; i++) {
printf("%d:", i);
for (Edge *p = first_edge[i]; p; p = p -> next)
if (!counterpart(p) -> cap && p -> dest != t) printf(" %d", p -> dest - k);
putchar('\n');
}
}
return 0;
}

ps. 这题的 lemon_judge 是我写的，一开始出了点偏差，很惭愧。发现问题后及时作了修正。

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120