洛谷 P4258 UOJ #171 【WC2016】挑战NPC
题解
建模:
把每个框子拆成三个点并连在一起,每颗球向他能匹配的框子的三个点分别连边。
之后求一般图最大匹配即可
证明:
如果原问题取得最优解,每颗球都会与一个框匹配,每个框如果匹配的球数不超过两个,则内部产生一条匹配边,这是一个最大匹配。
反过来,任意最大匹配中,每颗球必然能匹配,而后框子的“内部匹配边”会尽可能多,所以任意最大匹配一定是如上所述的一个匹配。
代码
#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;
}