1. 程式人生 > >UVA1327 && POJ1904 King's Quest(tarjan+巧妙建圖+強連通分量+縮點)

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