拓扑排序

概念

“对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。”——《百度百科》

上面这一段已经说得很清楚了

对于一个有向图,我们需要确定他节点的顺序,使得它的所有出边指向他后面的节点

图片来自AcWing
显然,一个有环的图是无法进行拓扑排序的

我们把可以进行拓扑排序的图成为一张拓扑图

显然拓扑图就得 DAG (有向无环图)

HOW?

可以运用 bfs

  1. 首先将所有入度为零的点入队
  2. 若队列非空
  3. 取出对头结点,并枚举它的每个出边
  4. 对每个出边的入度减一
  5. 如果这个节点入读变成了零,就把他压入队列

说白了就是一个删边、入队的过程,显然一个 DAG 可以将每个点入一次队

例题

AcWing1191家谱树

分析

裸的拓扑排序

把每个人当成一个节点

把父子关系转化成建边,有父亲指向儿子

跑一边拓扑排序,可以得出家谱关系

有SPJ

代码

/*************************************************************************
    > File Name: p1191家谱树.cpp
    > Author: typedef
    > Mail: 1815979752@qq.com 
    > Created Time: 2020/11/17 21:23:13
 ************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=107,M=N*N/2;
int n,m;
int h[N],e[N],ne[M],idx,d[N],q[N];
template<typename T>inline void read(T &n){
	T w=1;n=0;char ch=getchar();
	while(!isdigit(ch)&&ch!=EOF){if(ch=='-') w=-1;ch=getchar();}
	while(isdigit(ch)&&ch!=EOF){n=(n<<3)+(n<<1)+(ch&15);ch=getchar();}
	n*=w;
}
inline void topsort(){
	int hh=0,tt=-1;
	for(register int i=1;i<=n;i++)
		if(!d[i])
			q[++tt]=i;
	while(hh<=tt){
		int t=q[hh++];
		for(register int i=h[t];~i;i=ne[i]){
			int j=e[i];
			if(--d[j]==0) q[++tt]=j;
		}
	}
	return;
}
inline void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
	return;
}
int main(){
    double c1=clock();
	read(n);
	memset(h,-1,sizeof(h));
	for(register int i=1;i<=n;i++){
		int son=1;
		read(son);
		while(son){
			add(i,son);
			d[son]++;
			read(son);
		}
	}
	topsort();
	for(register int i=0;i<n;i++) printf("%d ",q[i]);
	puts("");
	double c2=clock();
	printf("%lfms\n",c2-c1);
	system("pause");
	return 0;
}

AcWing1192奖金

分析

差分约束的简化版本(大概

万恶的资本家,想尽办法压榨员工奖金

老师说,自己的问题就要自己解决

所以本篇题解到此为止

好吧,就是说,如果存在正环那么就无解

如果边权无限制,那么只好SPFA

本题边权为正,于是 topsort 就可以了

然后跑topsort求最长路

代码

/*************************************************************************
    > File Name: p1192奖金.cpp
    > Author: typedef
    > Mail: 1815979752@qq.com 
    > Created Time: 2020/11/17 22:21:14
 ************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=10005,M=20005;
int n,m;
int head[N],ver[M],nxt[M];
int d[N];//记录入读
int dist[N];
int tps[N];//topsort结果
int tot=0;
queue<int> q;
void add(int a,int b){
	ver[++tot]=b;
	nxt[tot]=head[a];
	head[a]=tot;
}
bool topsort(){
	int cnt=0;
	for(int i=1;i<=n;i++)
		if(!d[i])
			q.push(i),tps[++cnt]=i;
	while(q.size()){
		int t=q.front();
		q.pop();
		for(int i=head[t];i;i=nxt[i]){
			int j=ver[i];
			d[j]--;
			if(!d[j]) q.push(j),tps[++cnt]=j;
		}
	}
	if(cnt==n) return true;
	else return false;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		add(b,a);
		d[a]++;
	}
	if(!topsort()) puts("Poor Xed");
	else{//最长路求最小值
		for(int i=1;i<=n;i++) dist[i]=100;
		for(int i=1;i<=n;i++){
			int j=tps[i];
			for(int k=head[j];k;k=nxt[k])
				dist[ver[k]]=max(dist[ver[k]],dist[j]+1);
		}
		int res=0;
    	for(int i=1;i<=n;i++) res+=dist[i];
    	printf("%d\n",res);
	}
	return 0;
}
posted @ 2020-11-17 22:38  actypedef  阅读(52)  评论(0编辑  收藏  举报