UVA1327 && POJ1904 King's Quest(tarjan+巧妙建图+强连通分量+缩点)

1 篇文章 0 订阅

UVA1327
POJ1904


题意:

有n个王子,每个王子都有k个喜欢的妹子,每个王子只能和喜欢的妹子结婚。现有一个匹配表,将每个王子都与一个自己喜欢的妹子配对。请你根据这个表得出每个王子可以和几个自己喜欢的妹子结婚,按序号升序输出妹子的编号,这个表应满足所有的王子最终都有妹子和他结婚(一个妹子只能嫁给一个王子)。


看到题目时,一脸懵逼,只觉得题面很有(ci)趣(ji)

但没办法啊,为了王国的一夫一妻制我们只好努力啊awa


让我们首先来考虑建图

  • 如果王子u喜欢妹子v那我们可以从u向v连一条有向边

  • 如果妹子v可以与王子u配对(即在配对表上),那我们可以从v向u连一条有向边

对于样例

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4

我们建出了这样一张图:

红的是王子,蓝的是妹子,绿的表示从王子到公主,黄的表示从妹子到王子

仔细观察我们发现了这张图的一些特别之处:

每个紫色框出的部分都是个强连通分量!

这意味着什么?

这说明这个分量内的每个王子和这个分量内的每个妹子都可以随意匹配

答案只需要枚举王子和他所在分量内的妹子即可


而这个强连通分量又该咋求呢?

当然就是tarjan啦


tips1.UVA的题目是有多组数据的,POJ的每个点只有一组数据,下面的的代码以多组数据为例,但POJ上也能过


tips2.代码中王子编号1…n,妹子编号n+1…2n


/* read() write()可以换成你自己的快读快输,因为这题数据很多 */
template<class t> void Write(t x,char c){
    putchar(c);write(x);
}
const int N=2e3+5,M=2e5+N;
int en,h[N<<1],n,m,dfn[N<<1],low[N<<1],st[N<<1],num,cnt,bel[N<<1],top,ans[N];
struct edge{int n,v;}e[M]; //前向星存边
inline void add(const int &x,const int &y){e[++en]=(edge){h[x],y};h[x]=en;}
void tarjan(int x){ //tarjan求强连通分量
    st[++top]=x; //手打堆
    dfn[x]=low[x]=++num;
    for(int i=h[x];i;i=e[i].n){
        int y=e[i].v;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(!bel[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        cnt++;
        int TOP;
        do{
            TOP=st[top--];
            bel[TOP]=cnt; //表示每个王子的所属强连通分量
        }while(TOP!=x);
    }
}
signed main(){
    while(~scanf("%d",&n)){
        en=num=cnt=top=0;
        memset(h,0,sizeof h);
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(bel,0,sizeof bel);
        for(int i=1,k;i<=n;i++){
            read(k);
            for(int j=1,x;j<=k;j++){
                read(x);
                add(i,x+n); //从王子向妹子连边
            }
        }
        for(int i=1,x;i<=n;i++){
            read(x);
            add(x+n,i); //从妹子向王子连边
        }
        for(int i=1;i<=n<<1;i++) if(!dfn[i])
            tarjan(i); //跑tarjan
        for(int u=1;u<=n;u++){ //枚举王子
            int nm=0;
            for(int i=h[u];i;i=e[i].n){ //枚举王子喜欢的妹子
                int v=e[i].v;
                if(bel[u]==bel[v]) ans[++nm]=v-n; //判断王子和妹子是否在同一强连通分量中
            }
            sort(ans+1,ans+1+nm); //要求按妹子编号升序输出
            write(nm); //每个王子的可匹配妹子数
            for(int i=1;i<=nm;i++)
                Write(ans[i],' ');
            puts("");
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值