HDU 4685 Prince and Princess

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值