1. 程式人生 > >[學習][poj2367]拓撲序 Genealogical tree

[學習][poj2367]拓撲序 Genealogical tree

曾經,經常聽到大佬們一口一個拓撲序,覺得是個高深的問題,結果……TMD這麼簡單,名字幹嘛取得這麼高大上啊喂 [掀桌!

拓撲序的定義:
對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。——百度百科

說得那麼複雜,看看這位CSDN大佬的定義:
將有向圖中的頂點以線性方式進行排序。即對於任何連線自頂點u到頂點v的有向邊uv,在最後的排序結果中,頂點u總是在頂點v的前面。——dm_vincent

打個比方,如下圖(來自另一位CSDN大佬神奕
這裡寫圖片描述
那麼它的拓撲序就是{ 1, 2, 4, 3, 5 }(這張圖只有這一個拓撲序,比較特殊,其他圖可能會有很多,比如說下圖,來自dm_vincent
這裡寫圖片描述

知道了拓撲序的定義,我們來看看它的實現方法。

拓撲序的實現
目前我學習了兩種(知道為什麼我用了兩個CSDN大佬的節選嗎,沒錯,他倆一人一種方法,其實,還有bfs實現):dfs和佇列

dfs:建圖,把每一個點dfs,並從dfs最深處往回記錄(一定要是從最深處往回記錄啊啊啊啊),最後將記錄的陣列倒著輸出。

佇列:建圖,記錄所有入度為0的點,將他們壓入佇列,然後處理佇列,將佇列中的點指向的點入度減一,如果某個點入度減為零,就把它壓入佇列。最後輸出入隊順序。

題目描述
The system of Martians’ blood relations is confusing enough. Actually, Martians bud when they want and where they want. They gather together in different groups, so that a Martian can have one parent as well as ten. Nobody will be surprised by a hundred of children. Martians have got used to this and their style of life seems to them natural.
And in the Planetary Council the confusing genealogical system leads to some embarrassment. There meet the worthiest of Martians, and therefore in order to offend nobody in all of the discussions it is used first to give the floor to the old Martians, than to the younger ones and only than to the most young childless assessors. However, the maintenance of this order really is not a trivial task. Not always Martian knows all of his parents (and there’s nothing to tell about his grandparents!). But if by a mistake first speak a grandson and only than his young appearing great-grandfather, this is a real scandal.
Your task is to write a program, which would define once and for all, an order that would guarantee that every member of the Council takes the floor earlier than each of his descendants.

題目大意:一個火星人亂倫的故事)知道n個人,每個人都有一些子孫(也可能沒有),他們開會的時候要按長幼順序發言,輸出一種符合要求的發言順序排序。

輸入格式
標準輸入的第一行包含一個數字N,1 ≤ n ≤100。
接下來N行,第i行包含一個列表表示第i個節點的所有兒子節點。兒子節點列表是一組空格分隔的任意順序的數列。兒子節點名單可能是空的。
列表(即使是空的)以0結尾。

輸出格式
標準輸出一行拓撲序,每個節點用空格分開。如果幾個序列都滿足條件,只需寫出其中任意一個。

樣例資料
輸入

5
0
4 5 1 0
1 0
5 3 0
3 0

輸出

2 4 5 3 1

分析:拓撲序模板

程式碼:
dfs實現

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

int tot,cnt,n,p;
int first[110],nxt[10010],to[10010];
int s[110];
bool visit[110];

void addedge(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}

void dfs(int u)
{
    //就是這裡,之前把s[++cnt]=u放在這裡了,GG
    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(!visit[v])
        {
            visit[v]=1;
            dfs(v);
        }
    }

    s[++cnt]=u;//放在這裡才是從深處往回記
}

int main()
{
    freopen("lx.in","r",stdin);
    freopen("lx.out","w",stdout);

    n=getint();
    for(int i=1;i<=n;++i)
        while(scanf("%d",&p),p)
            addedge(i,p);

    for(int i=1;i<=n;++i)
        if(!visit[i])
        {
            visit[i]=1;
            dfs(i);
        }

    for(int i=cnt;i>=1;i--)
        printf("%d ",s[i]);

    return 0;
}

佇列實現

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

int tot,cnt,n,p;
int first[110],nxt[10010],to[10010];
int s[110],rudu[110];
queue<int> que;

void addedge(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}

int main()
{
    freopen("lx.in","r",stdin);
    freopen("lx.out","w",stdout);

    n=getint();
    for(int i=1;i<=n;++i)
        while(scanf("%d",&p),p)
        {
            rudu[p]++;
            addedge(i,p);
        }

    for(int i=1;i<=n;++i)
        if(rudu[i]==0)
            que.push(i);

    while(!que.empty())
    {
        int u=que.front();
        s[++cnt]=u;
        que.pop();
        for(int p=first[u];p;p=nxt[p])
        {
            int v=to[p];
            rudu[v]--;
            if(rudu[v]==0)
                que.push(v);
        }
    }

    for(int i=1;i<=cnt;++i)
        printf("%d ",s[i]);

    return 0;
}

本篇完。