[学习][poj2367]拓扑序 Genealogical tree

来源:互联网 发布:知乎 入门耳机 编辑:程序博客网 时间:2024/06/08 03:04

曾经,经常听到大佬们一口一个拓扑序,觉得是个高深的问题,结果……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的点,将他们压入队列,然后处理队列,将队列中的点指向的点入度减一,如果某个点入度减为零,就把它压入队列。最后输出入队顺序。

例题
题目背景
poj2367

题目描述
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;}

本篇完。