洛谷 P4258 UOJ #171 【WC2016】挑戰NPC
阿新 • • 發佈:2018-12-24
題解
建模:
把每個框子拆成三個點並連在一起,每顆球向他能匹配的框子的三個點分別連邊。
之後求一般圖最大匹配即可
證明:
如果原問題取得最優解,每顆球都會與一個框匹配,每個框如果匹配的球數不超過兩個,則內部產生一條匹配邊,這是一個最大匹配。
反過來,任意最大匹配中,每顆球必然能匹配,而後框子的“內部匹配邊”會盡可能多,所以任意最大匹配一定是如上所述的一個匹配。
程式碼
#include<bits/stdc++.h> using namespace std; const int maxm = 100 + 5; const int maxn = maxm * 6; const int maxe = 500000 + 5; int t; int n, m, e; int head[maxn], nxt[maxe * 2], to[maxe * 2], cnt; void inline addEdge(int u, int v) { nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt; nxt[++cnt] = head[v], to[cnt] = u, head[v] = cnt; } namespace Blossom { int pre[maxn]; int find(int x) { return pre[x] ? pre[x] = find(pre[x]) : x; } int type[maxn], que[maxn], front, tail, link[maxn], match[maxn], deep[maxn], fa[maxn]; int inline lca(int u, int v) { u = find(u), v = find(v); while(u != v) { if(deep[u] > deep[v]) u = find(fa[u]); else v = find(fa[v]); } return u; } void inline shrink(int u, int v, int e) { while(find(u) != e) { link[u] = v, v = match[u]; if(type[v] == 2) type[v] = 1, que[tail++] = v; if(find(u) == u) pre[u] = e; if(find(v) == v) pre[v] = e; u = link[v]; } } bool inline bfs(int s) { memset(pre, 0, sizeof pre); memset(type, 0, sizeof type); memset(link, 0, sizeof fa); memset(fa, 0, sizeof fa); front = 0, tail = 0; type[que[tail++] = s] = 1; deep[s] = 1; fa[s] = 0; while(front != tail) { int u = que[front++]; for(int e = head[u]; e; e = nxt[e]) { int v = to[e]; if(find(v) == find(u) || type[v] == 2) continue; if(!type[v]) { deep[v] = deep[u] + 1, fa[v] = u; type[v] = 2, link[v] = u; if(!match[v]) { for(int now = v, last; now; now = last) { last = match[link[now]]; match[now] = link[now], match[link[now]] = now; } return true; } type[match[v]] = 1, que[tail++] = match[v]; deep[match[v]] = deep[v] + 1, fa[match[v]] = v; } else if(type[v] == 1){ int e = lca(u, v); shrink(u, v, e); shrink(v, u, e); } } } return false; } int inline edmond() { int ans = 0; memset(match, 0, sizeof match); for(int i = 1; i <= n + 3 * m; ++i) if(!match[i]) ans += bfs(i); return ans; } } using namespace Blossom; void inline Init() { memset(head, 0, sizeof head); memset(to, 0, sizeof to); memset(nxt, 0, sizeof nxt); cnt = 0; scanf("%d %d %d", &n, &m, &e); int u, v; while(e--) { scanf("%d %d", &u, &v); addEdge(u, 3 * (v - 1) + n + 1); addEdge(u, 3 * (v - 1) + n + 2); addEdge(u, 3 * (v - 1) + n + 3); } for(int i = 1; i <= m; ++i) { int a = 3 * (i - 1) + n + 1, b = 3 * (i - 1) + n + 2, c = 3 * (i - 1) + n + 3; addEdge(a, b); addEdge(b, c); addEdge(c, a); } } int main() { scanf("%d\n", &t); while(t--) { Init(); printf("%d\n", edmond() - n); for(int i = 1; i <= n; ++i) printf("%d ", (match[i] - n - 1) / 3 + 1); putchar('\n'); } return 0; }