【6】拓扑排序学习笔记

前言

有向无环图和拓扑排序直接关联到中后期的图论建模思想,是很重要的基础知识。这个如果不彻底弄懂,以后图论会很困难。

有向无环图

正如其名,一个边有向没有环的图,也叫DAG。

DAG图实际运用 :描述含有公共子式 的表达式及工程系统的进行过程时的有效工具。

一个较大的工程被分成若干个子工程,这些子工程被称作活动。活动之间存在某种约束关系,用有向边来表示。

关心的问题:

1 1 1 :工程能否顺利进行

2 2 2 :估算整个工程完成所需要的最短时间

拓扑排序

用于在一个DAG表示的工程的的前后驱约束关系中,对完成活动的先后进行排序,使工程顺利进行。

拓扑排序的步骤:

1 1 1 :选择图中所有入度为 0 0 0 的点,加入栈/队列。

2 2 2 :取出栈顶/队首的点加入拓扑序列末尾,删除这个点以及其出边(出边所到点的入度减 1 1 1 ),弹栈/队列。

3 3 3 :在出边到达的点中选择入度为 0 0 0 的点,加入栈/队列。

4 4 4 :重复 2 ∼ 3 2\sim3 2∼3 ,直到栈/队列空。

如果拓扑排序结束后拓扑序列有 n n n( n n n 为图的顶点数)个元素,那么图中无环;否则,图中必定存在环。

时间复杂度: O ( n + m ) O(n+m) O(n+m)

cpp 复制代码
void topo_sort()
{
    int sta[300000],top=0;
    for(int i=1;i<=n;i++)
        if(ind[i]==0)sta[++top]=i;
    while(top)
        {
            int now=sta[top];
            top--;
            for(int i=h[now];i;i=e[i].next)
                {
                ind[e[i].v]--;
                if(ind[e[i].v]==0)sta[++top]=e[i].v;
                }
            ou[++ans]=now;
        }
}

拓扑排序例题

例题 1 1 1 :

B3644 家谱树

拓扑排序板子题,不多赘述。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct edge
{
    int v,next;
}e[300000];
int n,m,h[300000],cnt=0,ans=0,ind[300000],ou[300000];
void add_edge(int u,int v)
{
	e[++cnt].v=v;
	e[cnt].next=h[u];
	h[u]=cnt;
	ind[v]++;
}

void topo_sort()
{
	int sta[300000],top=0;
	for(int i=1;i<=n;i++)
	    if(ind[i]==0)sta[++top]=i;
	while(top)
	    {
	    	int now=sta[top];
	    	top--;
	    	for(int i=h[now];i;i=e[i].next)
	    	    {
	    	    ind[e[i].v]--;
	    	    if(ind[e[i].v]==0)sta[++top]=e[i].v;
	    	    }
	    	ou[++ans]=now;
		}
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        {
        	int t=-1;
        	while(t!=0)
        	    {
        	    scanf("%d",&t);
        	    if(t==0)break;
        	    add_edge(i,t);
        	    }
		}
	topo_sort();
	if(ans!=n)printf("-1");
	else 
	   for(int i=1;i<=ans;i++)
	       printf("%d ",ou[i]);
	return 0;
}

例题 2 2 2 :

U141986 奖金

前置知识:【6*】差分约束系统学习笔记

以每个人的奖金数为点构造差分约束系统,由于题目要求至少,需要求最长路,由于边权只有 1 1 1 ,可以通过拓扑排序直接求解最长路。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct edge
{
    int v,next;
}e[300000];
int n,m,h[300000],cnt=0,ans=0,ind[300000],ou[300000],dis[300000],sum=0;
void add_edge(int u,int v)
{
    e[++cnt].v=v;
    e[cnt].next=h[u];
    h[u]=cnt;
    ind[v]++;
}
 
void topo_sort()
{
    int sta[300000],top=0;
    for(int i=0;i<=n;i++)
        if(ind[i]==0)sta[++top]=i;
    while(top)
        {
            int now=sta[top];
            top--;
            for(int i=h[now];i;i=e[i].next)
                {
                ind[e[i].v]--;dis[e[i].v]=max(dis[e[i].v],dis[now]+1);
                if(ind[e[i].v]==0)sta[++top]=e[i].v;
                }
            ou[++ans]=now;
        }
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add_edge(y,x);
        }
    for(int i=1;i<=n;i++)
        dis[i]=-1;
    dis[0]=99;
    for(int i=1;i<=n;i++)
        add_edge(0,i);
    topo_sort();
    if(ans!=n+1)printf("Poor Xed");
    else
       {
       for(int i=1;i<=ans;i++)
           sum+=dis[i];
       printf("%d",sum);
       }
    return 0;
}

例题 3 3 3 :

P1983 [NOIP2013 普及组] 车站分级

图论建模题目,第一眼看上去像DP。

由于在一段内,车所经过的车站必然比没有经过的车站优先级高,利用这一点可以以每个车站的优先级为点构造差分约束系统,由于题目要求最少,需要求最长路,同时边权依旧只有 1 1 1 ,可以直接使用拓扑排序。

至于建边,可以把有效行程之内未经过的车站到每一个经过了的车站建一条边,表示一个大于关系。同时这样可能会有很多重边,可以通过记忆化,排除冗余。

当然,此题有复杂度更优秀的做法,可以参考其他题解。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int v,next,dis;
}e[1000010];
int n,m,s,h[1010],cnt=0,z[1010],book[1010],dis[1010],ind[1010],vis[1010][1010],ans=0;
void add_edge(int f,int v,int dis)
{
    e[++cnt].next=h[f];
    e[cnt].v=v;
    e[cnt].dis=dis;
    h[f]=cnt;
    ind[v]++;
}
 
void topo_sort()
{
    int sta[300000],top=0;
    for(int i=0;i<=n;i++)
        if(ind[i]==0)sta[++top]=i;
    while(top)
        {
            int now=sta[top];
            top--;
            for(int i=h[now];i;i=e[i].next)
                {
                ind[e[i].v]--;dis[e[i].v]=max(dis[e[i].v],dis[now]+e[i].dis);
                if(ind[e[i].v]==0)sta[++top]=e[i].v;
                }
        }
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
        {
            scanf("%d",&s);
            for(int j=0;j<s;j++)scanf("%d",&z[j]);
            for(int j=0;j<s;j++)book[z[j]]=1;
            for(int k=0;k<s;k++)
                for(int j=z[0];j<=z[s-1];j++)
                    if(!book[j]&&!vis[j][z[k]])
                       {
					   add_edge(j,z[k],1);
					   vis[j][z[k]]=1;
				       }
            memset(book,0,sizeof(book));
        }
    for(int i=1;i<=n;i++)
        add_edge(0,i,0);
    for(int i=1;i<=n;i++)
        dis[i]=-99999999;
    dis[0]=0;
    topo_sort();
    for(int i=1;i<=n;i++)
        ans=max(ans,dis[i]);
    printf("%d",ans+1);
    return 0;
}

后记

一篇教练推荐的博客,写在这里,提供参考:

拓扑排序(Topological Sorting)

相关推荐
微凉的衣柜1 分钟前
VICP(Velocity-based ICP):通过运动校准实现精准姿态估计
人工智能·算法·计算机视觉
抓饼先生10 分钟前
关于创建UNIX/Linux daemon进程的笔记
linux·笔记·unix
灏瀚星空24 分钟前
画布交互系统深度优化:从动态缩放、小地图到拖拽同步的全链路实现方案
经验分享·笔记·python·microsoft·交互
lkbhua莱克瓦2428 分钟前
用c语言实现——一个带头节点的链队列,支持用户输入交互界面、初始化、入队、出队、查找、判空判满、显示队列、遍历计算长度等功能
c语言·数据结构·程序人生·算法·链表·交互·学习方法
睡觉然后上课30 分钟前
FPGA(现场可编程门阵列)笔记
笔记·嵌入式硬件·fpga开发
虾球xz34 分钟前
游戏引擎学习第239天:通过 OpenGL 渲染游戏
c++·学习·游戏·游戏引擎
~卷心菜~37 分钟前
CSS基础-即学即用 -- 笔记1
前端·css·笔记
jonhswei38 分钟前
xss学习6
学习·xss
How_doyou_do40 分钟前
相对论大师-记录型正负性质BFS/图论-链表/数据结构
python·算法
奕天者42 分钟前
C++学习笔记(三十六)——STL之排序算法
c++·笔记·学习