Sample Input
2 4 4 2 1 2 2 1 2 2 2 3 2 3 4 1 2 2 1 2
Sample Output
Case #1: 2 1 2 2 1 2 1 3 1 4 Case #2: 2 1 2题意:有n个王子和m个公主,每个王子都喜欢若干个公主,王子只能和他喜欢的公主结婚,而公主可以和任一个王子结婚。给出每个王子喜欢的公主,这样可以得到一个最大配对数。对于每个王子,现在要分别输出每个王子能和哪些公主结婚,要求王子和这些公主中任一个结婚后,不会影响原先的最大配对数。思路:这道题和poj1904 (poj1904的解法)很相似,解法也是差不多的,但是这道题王子和公主的数量不一样,而且不一定有完美匹配。所以我们要虚拟出一些人出来构成完美匹配。先将每个王子与他喜欢的女人连一条有向边,求一次最大匹配。对于每个没有匹配到的王子,虚拟出一个公主,这个公主被所有王子喜欢,所以所有的王子都要连一条有向边到这个虚拟的公主。对于每个没有匹配到的公主,虚拟出一个王子,这个王子喜欢所有公主,所以这个王子要连一条有向边到所有的公主。最后对于每个公主(包括虚拟的),都要连一条有向边到与她配对的王子。构图完成后,缩点求强连通分量,每个王子都可以与和他在同一个强连通分量里的所有公主结婚,当然不能是虚拟的公主。#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <vector> #include <cmath> #include <cstdlib> #include <map> #define L(rt) (rt<<1) #define R(rt) (rt<<1|1) #define ll long long using namespace std; const int maxn=2005; struct node { int v,next; } edge[maxn*maxn]; int G[maxn],matchn[maxn],matchm[maxn],ans[maxn]; int low[maxn],dfn[maxn],scc[maxn],stack[maxn]; bool vis[maxn],ins[maxn]; int n,m,num,nn,top,snum,cnt,tot; void init() { memset(G,-1,sizeof(G)); num=0; } void add(int u,int v) //建动态链表,有向图 { edge[num].v=v; edge[num].next=G[u]; G[u]=num++; } void input() { int k,v; scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) { scanf("%d",&k); while(k--) { scanf("%d",&v); add(i,v+n); //公主标号是n+1~n+m } } } bool find(int u) //匹配 { for(int i=G[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; if(!vis[v]) { vis[v]=true; if(matchm[v]==-1||find(matchm[v])) { matchm[v]=u; //公主v匹配王子u matchn[u]=v; //王子u匹配公主V return true; } } } return false; } void MMG() { int ans=0; memset(matchn,-1,sizeof(matchn)); memset(matchm,-1,sizeof(matchm)); for(int i=1; i<=n; i++) { memset(vis,false,sizeof(vis)); if(find(i)) ans++; } } void make_graph() //扩图 { tot=n+m; for(int i=1; i<=n; i++) if(matchn[i]==-1) //王子没有匹配到公主的 { ++tot; //建一个虚拟公主 matchn[i]=tot; matchm[tot]=i; for(int k=1; k<=n; k++) //并且这个公主受所有王子喜欢.. add(k,tot); } for(int i=n+1; i<=n+m; i++) if(matchm[i]==-1) //公主没有王子跟她匹配的 { ++tot; //建虚拟王子 matchn[tot]=i; matchm[i]=tot; for(int k=n+1; k<=n+m; k++) add(tot,k); //王子是喜欢所有公主的 } for(int i=1; i<=tot; i++) //找到公主有匹配的,建公主到王子的有向边 if(matchm[i]!=-1) add(i,matchm[i]); } void dfs(int u) //球强连通分量 { int x; low[u]=dfn[u]=++cnt; stack[top++]=u; ins[u]=true; for(int i=G[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; if(!dfn[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(ins[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) //得到一个关节点, { snum++; //一个强连通分量 do //这个图分量的成员出栈 { x=stack[--top]; ins[x]=false; scc[x]=snum; //x是在snum图分量中 } while(x!=u); } } void tarjan() { memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ins,false,sizeof(ins)); top=cnt=snum=0; for(int i=1; i<=tot; i++) if(!dfn[i]) dfs(i); } void solve() { for(int u=1; u<=n; u++) { int cc=0; for(int i=G[u]; i!=-1; i=edge[i].next) //每个王子,能取的公主 { int v=edge[i].v; if(scc[u]==scc[v]&&v<=n+m) //王子和公主在一个图分量 , 且这个公主不是虚拟公主 ans[cc++]=v-n; // v-n 个公主 } sort(ans,ans+cc); //从小到大输出 printf("%d",cc); for(int i=0; i<cc; i++) printf(" %d",ans[i]); printf("\n"); } } int main() { //freopen("1","r",stdin); int t,c=0; scanf("%d",&t); while(t--) { init(); input(); MMG(); make_graph(); tarjan(); printf("Case #%d:\n",++c); solve(); } return 0; }