- 题意: n个王子和m个公主进行匹配,王子只能和喜欢的公主配对,公主可以跟任意王子配对,问每个王子最多能配对到几个公主且选择任意的公主不会影响匹配数量
- 思路: 先构造出完美匹配的图,这样王子可以选择在同一个强联通分量里的公主而不会影响其他王子.(因为是二分图,强联通分量肯定有k个王子和k个公主). 为了构造完美匹配,先跑一遍匹配,为每个没有配对的王子设置一个
虚拟公主,为每个没有配对的公主设置一个虚拟王子,王子连边他喜欢的公主,公主连边她匹配的王子.
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2e3+10;
int n,m;
struct Edge{
int to,nxt;
}edge[N*N*10];
int tot,head[N];
int dfn[N],low[N],fa[N];
stack<int> st;
vector<int> ans;
int ind,ffa;
void init(){
tot = 0; ind = ffa = 0;
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(fa,0,sizeof fa);
}
void add(int u,int v){
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
int uN,vN,ly[N],lx[N];
bool used[N];
bool dfs(int u){
for(int i=head[u];~i;i=edge[i].nxt){
int v = edge[i].to;
if(used[v]) continue;
used[v] = 1;
if(ly[v]==-1 || dfs(ly[v])){
ly[v] = u; lx[u] = v;
return true;
}
}
return false;
}
int hungary(){
int res = 0;
memset(ly,-1,sizeof ly);
memset(lx,-1,sizeof lx);
for(int u=1;u<=uN;u++){
memset(used,0,sizeof used);
if(dfs(u))res++;
}
return res;
}
void tarjan(int u){
int v;
dfn[u] = low[u] = ++ind;
st.push(u);
for(int i =head[u];i!=-1;i=edge[i].nxt){
v = edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(!fa[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(low[u]== dfn[u]){
ffa++;
do{
v = st.top(); st.pop();
fa[v] = ffa;
}while(v!=u);
}
}
void solve(int cas){
printf("Case #%d:
",cas);
scanf("%d%d",&n,&m);
int to,k;
init();
for(int i=1;i<=n;++i){
scanf("%d",&k);
for(int j=1;j<=k;++j){
scanf("%d",&to);
add(i,to+n);
}
}
uN = n, vN = m;
hungary();
int all = n+m;
for(int i=1;i<=n;++i)if(lx[i]==-1){
all++; // 加虚拟公主
lx[i] = all; ly[all] = i; // 匹配
for(int j=1;j<=n;++j) add(j,all); // 每个王子都喜欢虚拟公主
}
for(int i=1;i<=m;++i)if(ly[i+n]==-1){
all++; // 加虚拟王子
lx[all] = i+n; ly[i+n] = all;
for(int j=1;j<=m;++j) add(all,j+n); // 这个王子喜欢每个公主
}
for(int i=1;i<=all;++i) if(lx[i]!=-1) add(lx[i],i);
for(int i=1;i<=all;++i)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;++i){
ans.clear();
for(int j=head[i];~j;j=edge[j].nxt){
int v = edge[j].to;
if(v>n+m) continue;
if(fa[v]==fa[i]) ans.push_back(v-n);
}
sort(ans.begin(),ans.end());
printf("%d",(int)ans.size());
for(int j=0;j<(int)ans.size();++j){
printf(" %d",ans[j]);
}
puts("");
}
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;++i){
solve(i);
}
return 0;
}