hdu 4685 (最大匹配+强连通分量)

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; }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值