题意:n个球,放进m个盒子,每个盒子最多放三个球,每个球可以放的盒子是一个集合,半空的盒子是指盒子内的球个数<= 1的盒子。求半空的盒子数目的最大值。
一般图最大匹配。
把每个盒子分成三个,三个间两两连边,每个点向他所能到的盒子的三个分点连边,跑最大匹配,最后的答案就是,maxmatch - n。
这个证明比较容易,但是非常难想,我还需要研究一下这之间的思维过程。
学了一下带花树算法,可能一知半解。
大致思路就是,我们增广的时候,把奇环缩成一个点然后当二分图做,实现的过程利用BFS,寻找奇环,缩点并且扩展新的点。 思路是这样,实现时用并查集实现。
#include <bits/stdc++.h>
using namespace std;
const int N = 666;
int n , m , x , y , mat[N] , pre[N] , fa[N] , ty[N] , e , n2;
bool way[N][N] , inq[N];
void add(int x , int y) {
way[x][y] = way[y][x] = 1;
}
int getf(int x) {
return (fa[x] == x) ? x : fa[x] = getf(fa[x]);
}
void init(void) {
for(int i = 1;i <= n2;++ i) fa[i] = i , pre[i] = ty[i] = 0;
}
bool vis[N];
int LCA(int x , int y) {
memset(vis , 0 , sizeof(vis));
for(int i = x;i;i = pre[mat[i]]) i = getf(i) , vis[i] = 1;
for(int i = y;i;i = pre[mat[i]]) {i = getf(i); if(vis[i]) return i;}
}
queue <int> q;
void shrink(int x , int y , int lca) {
while(getf(x) != lca) {
pre[x] = y; y = mat[x];
if(ty[y] == 2) q.push(y); ty[y] = 1;
fa[x] = fa[y] = lca;
x = pre[y];
}
}
int aug(int x) {
memset(inq , 0 , sizeof(inq)); init();
while(!q.empty()) q.pop();
q.push(x); inq[x] = 1; ty[x] = 1;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 1;i <= n2;++ i) if(way[u][i]) {
if(ty[i] == 2 || getf(i) == getf(u)) continue;
if(!ty[i]) {
pre[i] = u; ty[i] = 2;
if(!mat[i]) {
for(int a = i , la , b;a;a = la) {
la = pre[a];b = mat[la]; mat[a] = la; mat[la] = a;la = b;
}
return 1;
}
ty[mat[i]] = 1; q.push(mat[i]); inq[mat[i]] = 1;
}
else {
int lca = LCA(u , i);
shrink(u , i , lca); shrink(i , u , lca);
}
}
}
return 0;
}
void onedance(void) {
memset(mat , 0 , sizeof(mat)); memset(way , 0 , sizeof(way));
int ans = 0;
scanf("%d%d%d" , &n , &m , &e);
n2 = n + 3 * m;
for(int i = 1;i <= e;++ i) {
scanf("%d%d" , &x , &y);
for(int j = 1;j <= 3;++ j) add(x , n + 3 * y - 3 + j);
}
for(int i = 1;i <= m;++ i) {
for(int j = 1;j <= 3;++ j) {
for(int k = j + 1;k <= 3;++ k) add(n + 3 * i - 3 + j , n + 3 * i - 3 + k);
}
}
for(int i = 1;i <= n2;++ i) if(!mat[i]) ans += aug(i);
cout << ans - n << endl;
for(int i = 1;i <= n;++ i) {
printf("%d " , (mat[i] - n + 3 - 1) / 3);
}
puts("");
}
main(void) {
int t;
for(scanf("%d" , &t);t --;) {
onedance();
}
}