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; }