题意:
有n个王子,m个公主,王子可以喜欢很多个公主,每个王子可以娶一个公主,每个公主可以嫁一个王子,这样可以得到一个最大匹配,对每个王子,求出他选择娶那些公主不影响到最大匹配。
解法:
一开始,想到的做法是所有的王子向其喜欢的公主连边,做一遍二分图后,每个公主向选择了她的王子连边,这样,如果某个王子喜欢的两个公主在同一个强联通分量里的话,说明这个强联通分量里面的边都能取反向,则同一强联通分量里的公主和王子存在另一种方式配对,但是,这种方法处理不了某些王子娶不到公主,或者公主嫁不出去的情况。
考虑如何将上述情况转成所有公主和王子都能终成眷属的情况。对于每一个娶不到老婆的王子都弄一个虚拟的公主,这个公主被所有的王子喜欢(让这个王子和他可以娶到的真的公主在同一个强联通分量),对于每一个嫁不出了的公主都弄一个虚拟的王子,这个王子喜欢所有的公主,同理。
实现代码的时候,可以省去王子向公主连边,改成王子要娶的公主向他喜欢的其他的公主连边。
代码写的很丑,写了两个二分图匹配,跑得很慢==,懒得删掉第二次匹配了
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 1005
int n,m,N,n2,m2;
bool g[maxn][maxn],state[maxn],insta[maxn];
int cnt,top,ind,to[maxn],result[maxn],dfn[maxn],low[maxn],sta[maxn],bel[maxn],tmp[maxn];
struct node
{
int v,next;
}e[maxn*maxn];
int ecnt,pre[maxn];
void add(int u,int v)
{
e[ecnt].v=v;
e[ecnt].next=pre[u];
pre[u]=ecnt++;
}
int find(int x)
{
for (int i=1;i<=m;i++)
if (!state[i]&&g[x][i])
{
state[i]=1;
if (!result[i]||find(result[i]))
{
result[i]=x;
return 1;
}
}
return 0;
}
int hungry()
{
int res=0;
memset(result,0,sizeof(result));
for (int i=1;i<=n;i++)
{
memset(state,0,sizeof(state));
if (find(i)) res++;
}
return res;
}
void tarjan(int u)
{
dfn[u]=low[u]=++ind;
sta[++top]=u;
insta[u]=1;
for (int i=pre[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if (!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (insta[v])
low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u])
{
++cnt;
int v;
do
{
v=sta[top--];
insta[v]=0;
bel[v]=cnt;
}while (u!=v);
}
}
void solve()
{
top=cnt=ind=0;
memset(dfn,0,sizeof(dfn));
for (int i=1;i<=2*N;i++)
if (!dfn[i]) tarjan(i);
}
int main()
{
//freopen("/home/christinass/code/in.txt","r",stdin);
int cas,k,t;
scanf("%d",&cas);
for (int h=1;h<=cas;h++)
{
scanf("%d%d",&n,&m);
ecnt=0;
N=max(n,m);
memset(g,0,sizeof(g));
memset(pre,-1,sizeof(pre));
for (int i=1;i<=n;i++)
{
scanf("%d",&k);
for (int j=1;j<=k;j++)
{
scanf("%d",&t);
g[i][t]=1;
}
}
int ans=hungry();
memset(to,0,sizeof(to));
n2=n,m2=m;
if (ans<n||ans<m)
{
for (int i=1;i<=m;i++)
to[result[i]]=i;
for (int i=1;i<=n;i++)
if (!to[i])
{
++m2;
for (int j=1;j<=n;j++) g[j][m2]=1;
}
for (int i=1;i<=m;i++)
if (!result[i])
{
++n2;
for (int j=1;j<=m;j++) g[n2][j]=1;
}
}
swap(n,n2);swap(m,m2);
N=hungry();
for (int i=1;i<=N;i++)
{
int u=result[i];
to[u]=i;
for (int j=1;j<=N;j++)
if (g[u][j]&&j!=i)
add(i,j);
}
printf("Case #%d:\n",h);
solve();
for (int i=1;i<=n2;i++)
{
int u=to[i],cnt=0;
for (int j=1;j<=m2;j++)
if (g[i][j]&&bel[u]==bel[j]) tmp[cnt++]=j;
printf("%d%s",cnt,cnt?" ":"\n");
for (int j=0;j<cnt;j++)
printf("%d%s",tmp[j],j+1==cnt?"\n":" ");
}
}
return 0;
}