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

UVA1327 King's Quest

POJ1904 King's Quest


题意:

有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("");
        }
    }
}

posted @ 2019-07-18 23:29  think_twice  阅读(257)  评论(0编辑  收藏  举报