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

来源:互联网 发布:数据采集卡的使用 编辑:程序博客网 时间:2024/06/01 10:52
Sample Input
24 42 1 22 1 22 2 32 3 41 22 1 2
 

Sample Output
Case #1:2 1 22 1 21 31 4Case #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 longusing 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;}