vfk出的(好)题。赛时无人AC,一大堆人60分...
前两个点,暴搜即可。
第三个点,贪心。
第四到六个点,最大流。
std:带花树
把一个篮子拆为3个槽,每两个槽之间互相连边(vfk:无关紧要的小优化,只连其中两个槽即可),每个球与可放的篮子的三个槽都连边,直接跑带花树即可。
答案减去球的个数就是答案。
附vfk讲解:
筐子内装的球不超过 3 个意味着可以看做每个筐子有三个槽,每个槽可以放一个球,于是就变成了球和槽进行匹配,b 1 k , b 2 k , b 3 k 就代表了这三个槽。
原问题的解对应一个匹配:如果 b 1 k , b 2 k , b 3 k 中有不超过 1 个匹配点,那么三元环内部可以产生一条匹配边;如果匹配点超过 1 个则内部无法产生一条匹配边。原问题的解可以对应到一个
匹配数 = 半空袋子数 + n 的匹配。
最大匹配对应一个原问题的解:先依次从 a 1 , . . . , a n 出发找增广路,再依次从筐子对应的结点出发找增广路,这样可以求得一个 a k均为匹配点的最大匹配,显然这对应了原问题的一个解。
上文中说,“把 b 1 k , b 2 k , b 3 k 连成一个三元环”,事实上,只要连边就行了。(b 1 k , b 2 k )
这是因为同一个筐子的槽是等价的。这样,一个最大匹配中某个筐子对应的结点中有一个匹配点的时候可以让 b 3 k 成为匹配点。
PS:附vfk的图
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 605, maxm = 100005, maxq = 10000;
int n, m, e, head[maxn], cnt, tot, match[maxn], fri[maxn], fa[maxn], top[maxn], ring[maxn], q[maxq];
bool odd[maxn], vis[maxn];
struct _edge {
int v, next;
} g[maxm << 1];
inline int iread() {
int f = 1, x = 0; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return f * x;
}
inline void add(int u, int v) {
g[cnt] = (_edge) {v, head[u]};
head[u] = cnt++;
}
inline int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int h, t, clo;
inline int lca(int x, int y) {
clo++;
for(; x; x = find(top[x])) ring[x] = clo;
for(x = y; ring[x] != clo; x = find(top[x]));
return x;
}
inline void blossom(int x, int y, int p) {
for(; find(x) != find(p); x = fri[y]) {
fri[x] = y; y = match[x];
fa[find(x)] = fa[find(y)] = p;
q[t++] = y; odd[y] = 0;
}
}
inline bool dfs(int s) {
for(int i = 1; i <= tot; i++) vis[i] = odd[i] = fri[i] = top[i] = 0, fa[i] = i;
h = t = 0;
vis[q[t++] = s] = 1;
while(h != t) {
int now = q[h++];
for(int i = head[now]; ~i; i = g[i].next) {
int v = g[i].v;
if(!vis[v]) {
top[v] = fri[v] = now; odd[v] = vis[v] = 1;
if(!match[v]) {
for(int x, y, j = v; j; ) {
x = fri[j]; y = match[x];
match[j] = x; match[x] = j; j = y;
}
return 1;
}
vis[match[v]] = 1; top[match[v]] = v; q[t++] = match[v];
} else if(find(now) != find(v) && !odd[v]) {
int p = lca(now, v);
blossom(now, v, p); blossom(v, now, p);
}
}
}
return 0;
}
int main() {
int T = iread();
while(T--) {
for(int i = 0; i < maxn; i++) head[i] = -1, match[i] = 0; cnt = 0;
n = iread(); m = iread(); e = iread(); tot = n + m * 3;
for(int i = 1; i <= e; i++) {
int x = iread(), y = iread();
for(int j = 1; j <= 3; j++) add(x, n + 3 * (y - 1) + j), add(n + 3 * (y - 1) + j, x);
}
for(int i = 1; i <= m; i++) add(n + 3 * (i - 1) + 1, n + 3 * (i - 1) + 2), add(n + 3 * (i - 1) + 2, n + 3 * (i - 1) + 1);
int ans = 0;
for(int i = 1; i <= tot; i++) if(!match[i]) ans += dfs(i);
ans -= n;
printf("%d\n", ans);
for(int i = 1; i <= n; i++) printf("%d ", (match[i] - n - 1) / 3 + 1);
printf("\n");
}
return 0;
}