2013 暑假多校训练 8 总结
题目
题意:
有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; }