题目:
有 n 个球,用整数 1 到 n 编号。还有m 个筐子,用整数 1 到 m 编号。
每个筐子最多能装 3 个球。
每个球只能放进特定的筐子中。具体有 e 个条件,第 i 个条件用两个整数vi 和 ui 描述,表示编号为 vi 的球可以放进编号为 ui 的筐子中。
每个球都必须放进一个筐子中。如果一个筐子内有不超过 1 个球,那么我们称这样的筐子为半空的。
求半空的筐子最多有多少个,以及在最优方案中,每个球分别放在哪个筐子中。
分析:
非常巧妙的建模,把每一个筐拆成三个点,三个点之间互相连边。对于与筐相连的球,把球与对应筐的三个点相连,若最大匹配为maxmatch,那么发现答案就是n-maxmatch。
由于不是二分图,不能用匈牙利算法,而需要用带花树。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1010;
int n,m;
struct blossemtree{
int fa[maxn],link[maxn],Next[maxn],type[maxn],vis[maxn];
vector<int>g[maxn];
queue<int>q;
int n;
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void init(){
memset(link,0,sizeof(link));
for(int i=1;i<=n;i++) g[i].clear();
}
void add(int x,int y){
g[x].push_back(y);g[y].push_back(x);
}
void combine(int x,int lca){
while(x!=lca){
int u=link[x],v=Next[u];
if(find(v)!=lca) Next[v]=u;
if(type[u]==1){
type[u]=2;
q.push(u);
}
fa[find(x)]=find(u);fa[find(u)]=find(v);
x=v;
}
}
void contrack(int x,int y){
int lca=x;
memset(vis,0,sizeof(vis));
for(int i=x;i;i=Next[link[i]]){
i=find(i);vis[i]=1;
}
for(int i=y;i;i=Next[link[i]]){
i=find(i);
if(vis[i]){
lca=i;break;
}
}
if(find(x)!=lca) Next[x]=y;
if(find(y)!=lca) Next[y]=x;
combine(x,lca);
combine(y,lca);
}
void bfs(int S){
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) fa[i]=i;
memset(type,0,sizeof(type));
memset(Next,0,sizeof(Next));
type[S]=1;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<g[x].size();i++){
int y=g[x][i];
if(find(x)==find(y) || link[x]==y || type[y]==1) continue;
if(type[y]==2) contrack(x,y);
else if(link[y]){
Next[y]=x;
type[y]=1;
type[link[y]]=2;
q.push(link[y]);
}else{
Next[y]=x;
int pos=y,u=Next[y],v=link[u];
while(pos){
link[pos]=u;link[u]=pos;
pos=v;u=Next[pos];v=link[u];
}
return;
}
}
}
}
int maxmatch(int _n){
n=_n;
for(int i=1;i<=n;i++) if(!link[i]) bfs(i);
int ans=0;
for(int i=1;i<=n;i++) if(link[i]) ans++;
return ans/2;
}
}T;
int main(){
int kase;
int e;
scanf("%d",&kase);
while(kase--){
T.init();
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=m;i++){
int t=n+(i-1)*3;
T.add(t+1,t+2);
}
for(int i=1;i<=e;i++){
int u,v;
scanf("%d%d",&u,&v);
for(int j=1;j<=3;j++){
T.add(u,n+(v-1)*3+j);
}
}
printf("%d\n",T.maxmatch(n+m*3)-n);
for(int i=1;i<=n;i++){
printf("%d%c",(T.link[i]-n-1)/3+1,i==n?'\n':' ');
}
}
return 0;
}
^_^