【hdu】4685 Prince and Princess【二分匹配+tarjan】

本文介绍了一道组合匹配问题的解题思路,通过引入虚拟节点将不完美匹配转化为完美匹配问题,并利用二分匹配算法和强连通组件进行求解。详细解释了如何构造图模型以及如何通过Tarjan算法找到强连通组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:有n个王子,m个公主,每个王子有他自己喜欢的公主们,问一个王子可以匹配的公主有哪些,要求一个王子匹配一个公主后,总的可匹配数不能减少,也就是不能让别的王子的本来可以有老婆的,现在没了。

题解:题意和poj1904集合一样,只不过那题给出了完美匹配,且王子与公主都是n个,这题又加了限制,是poj1904的加强版,可以先试试那题再来做这题后好理解很多,这是poj1904的题解 http://blog.youkuaiyun.com/a709743744/article/details/52133778

现在我们在上述题解的情况下继续讲这题怎么做,我们把这题尽量转换成poj1904的形式,我们先对题目给出的条件求一次二分匹配,

假设匹配数是cnt,

对于未匹配的n-cnt个王子,我们虚拟出n-cnt个公主和他匹配,并让所有真实王子都喜欢这个虚拟公主

对于未匹配的m-cnt个公主,我们虚拟出m-cnt个王子和她匹配,并让虚拟王子喜欢所有的真实公主

这样王子和公主都有n+m-cnt个,问题就完全转换成了poj1904,然后我们就按poj1904求一遍强连通,记得最后别输出虚拟公主即可

#include<queue>
#include<vector>
#include<cstdio>//先用强连通缩点来化简图,然后在图上做拓扑排序,如果排序过程中,出现1个以上的点入度同时为0时,那么就不满足条件。
#include<cstring>
#include<algorithm>
#define PB push_back
#define MP make_pair
using namespace std;
const int N=4010;
const int inf=0x3f3f3f3f;
int dfn[N],low[N],instack[N],belong[N],S[N];
int index,scc,shead;
vector<int>G[N];
vector<pair<int,int> >E[N];
void dfs(int u)
{
    dfn[u]=low[u]=++index;
    S[shead++]=u;
    instack[u]=true;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[v],low[u]);
        }else if(instack[v])
            low[u]=min(dfn[v],low[u]);
    }
    if(low[u]==dfn[u]){
        scc++;
        while(1){
            int tmp=S[--shead];
            belong[tmp]=scc;
            instack[tmp]=false;
            if(tmp==u)break;
        }
    }
}
void tarjan(int n)
{
    index=scc=shead=0;
    memset(dfn,0,sizeof(dfn));
    memset(instack,false,sizeof(instack));
    for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);
}
int ans[N],lx[N],ly[N],used[N],n,m;
bool Dfs(int u)
{
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(!used[v]){
            used[v]=true;
            if(ly[v]==-1||Dfs(ly[v])){
                ly[v]=u;
                lx[u]=v;
                return true;
            }
        }
    }
    return false;
}
int hungry(int n)
{
    int res=0;
    memset(lx,-1,sizeof(lx));
    memset(ly,-1,sizeof(ly));
    for(int i=1;i<=n;i++){
        memset(used,0,sizeof(used));
        if(Dfs(i))res++;
    }
    return res;
}
int main()
{
    int T,kase=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=(n+m)*2;i++)G[i].clear();
        for(int i=1;i<=n;i++){
            int x,y;
            scanf("%d",&x);
            while(x--){
                scanf("%d",&y);
                G[i].PB(n+y);
            }
        }
        hungry(n);
        int all=n+m;
        for(int i=1;i<=n;i++)if(lx[i]==-1){//新增妹子
            all++;
            lx[i]=all;
            ly[all]=i;
            for(int j=1;j<=n;j++)G[j].PB(all);
        }
        for(int j=1;j<=m;j++)if(ly[j+n]==-1){//新增王子
            all++;
            lx[all]=j+n;
            ly[j+n]=all;
            for(int j=1;j<=m;j++)G[all].PB(j+n);
        }
        for(int i=1;i<=all;i++)
            if(lx[i]!=-1)G[lx[i]].PB(i);
        tarjan(all);
        printf("Case #%d:\n",++kase);
        for(int i=1;i<=n;i++){
            int cnt=0;
            for(int j=0;j<G[i].size();j++){
                int v=G[i][j];
                if(belong[i]==belong[v]){
                    if(v-n<=m)ans[cnt++]=v-n;
                }
            }
            sort(ans,ans+cnt);
            printf("%d",cnt);
            for(int j=0;j<cnt;j++){
                printf(" %d",ans[j]);
            }
            printf("\n");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值