第一节——拓扑排序

第一节——拓扑排序


本蒟蒻的第一篇文章,欢迎各位大佬来喷

一、前置知识

本文需要用到图、队列和邻接表的相关知识,不会的可以看看别人的,我找了几篇:

队列
链式前向星

二、拓扑排序

简介

拓扑排序是对一个有向图构造拓扑序列,解决工程是否能顺利进行的问题。构造时有 2 种结果:

1.此图全部顶点被输出:说明说明图中无「环」存在, 是 AOV 网
2.没有输出全部顶点:说明图中有「环」存在,不是 AOV 网
AOV(Activity On Vertex Network) :一种 有向 无回路 的图

应用

举个例子,比如早上你起床,只能穿衬衫才能穿外衣,不能穿了外衣再穿衬衫。
生活中也有类似的情况,完成了A事才能完成B事,完成了C事才能完成D事,拓扑排序就是完成这类问题的。

代码实现

原理

为了方便理解,我画了个图

开始拓扑排序!
1.找入度为0的点,入队列;
2.不断从队头弹点,把有关的边全删掉,相邻的点入度减1,如果有点入度为0了,入队列;
3.如果队列弹没了,结束;
4.如果还有点的入度>0,就说明有环,没有答案。
过程:




注释:
1.节点上的方框代表这个节点上的入度;
2.连在一起的方框是队列;
因为言(wo)简(bi)意(jiao)赅(lan),有些不删边的步骤我就省掉了

代码
#include<bits/stdc++.h>
#define PQG priority_queue < ll , vector < ll > , greater < ll > >
#define PQL priority_queue < ll , vector < ll > , less < ll > >
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ULL_MAX numeric_limits < ull > :: max()
#define LL_MAX numeric_limits < ll > :: max()
#define rand_ srand(int(time(NULL)))
#define log(a,b) log(a) / log(b)
#define ull unsigned long long
#define NP next_pemurtation
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long 
using namespace std;
int n,m,id,in[100001],head[100001];
queue <int> ans;//答案
struct edge{//链式前向星
	ll u,v,next;
}e[100001];
void add(ll u,ll v){
	e[++id].v = v;
	e[id].u = u;
	e[id].next = head[u];
	head[u] = id;
}
inline ll re(){
    ll x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= f;
    return x;
}
inline void write(ll x){
	if (x < 0){putchar('-'); x = -x;}
	ull y = 10, len = 1;
	while (y <= x){y *= 10; len++;}
	while (len--){y /= 10; putchar(x / y + 48); x %= y;}
}
void top_sort(){
	queue <int> q;
	int i;
	rep(i,1,n,1) if(!in[i]) q.push(i),ans.push(i);
	while(!q.empty()){
		ll u = q.front(); q.pop();
		for(i = head[u];i;i = e[i].next){
			ll v = e[i].v; in[v]--;
			if(!in[v]) q.push(v),ans.push(v);
		}
	}
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	n = re(),m = re();
	int u,v;
	for(int i = 1;i <= m;i++){
		u = re(),v = re();
		add(u,v);//建边
		in[v]++;//入度增加
	}
	top_sort();
	while(!ans.empty()){
		write(ans.front());
		putchar(' ');
		ans.pop();
	} 
	return 0;
} 
//码风有一(亿)点点诡异

运行结果:
如果题目叫你输出字典序最小的答案呢?
很简单,我给你两种方法:
1.队列秒变小根堆

#include<bits/stdc++.h>
#define PQG priority_queue < ll , vector < ll > , greater < ll > >
#define PQL priority_queue < ll , vector < ll > , less < ll > >
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ULL_MAX numeric_limits < ull > :: max()
#define LL_MAX numeric_limits < ll > :: max()
#define rand_ srand(int(time(NULL)))
#define log(a,b) log(a) / log(b)
#define ull unsigned long long
#define NP next_pemurtation
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long 
using namespace std;
int n,m,id,in[100001],head[100001];
queue <int> ans;
struct edge{
	ll u,v,next;
}e[100001];
void add(ll u,ll v){
	e[++id].v = v;
	e[id].u = u;
	e[id].next = head[u];
	head[u] = id;
}
inline ll re(){
    ll x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= f;
    return x;
}
inline void write(ll x){
	if (x < 0){putchar('-'); x = -x;}
	ull y = 10, len = 1;
	while (y <= x){y *= 10; len++;}
	while (len--){y /= 10; putchar(x / y + 48); x %= y;}
}
void top_sort(){
	PQG q = PQG ();
	int i;
	rep(i,1,n,1) if(!in[i]) q.push(i);
	while(!q.empty()){
		int u = q.top(); ans.push(u); q.pop();
		for(i = head[u];i;i = e[i].next){
			int v = e[i].v; in[v]--;
			if(!in[v]) q.push(v);
		}
	}
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	n = re(),m = re();
	int i,u,v;
	rep(i,1,m,1){
		u = re(),v = re();
		add(u,v); in[v]++;
	}
	top_sort();
	rep(i,1,n,1) if(in[i]){puts("-1"); return 0;}
	while(!ans.empty()){
		write(ans.front());
		ans.pop();
		putchar(' ');
	} 
	return 0;
} 

运行结果:

2.反向建边+队列秒变大根堆+反着输出

#include<bits/stdc++.h>
#define PQG priority_queue < ll , vector < ll > , greater < ll > >
#define PQL priority_queue < ll , vector < ll > , less < ll > >
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ULL_MAX numeric_limits < ull > :: max()
#define LL_MAX numeric_limits < ll > :: max()
#define rand_ srand(int(time(NULL)))
#define log(a,b) log(a) / log(b)
#define ull unsigned long long
#define NP next_pemurtation
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long 
using namespace std;
int n,m,id,in[100001],head[100001];
stack <int> ans;
struct edge{
	ll u,v,next;
}e[100001];
void add(ll u,ll v){
	e[++id].v = v;
	e[id].u = u;
	e[id].next = head[u];
	head[u] = id;
}
inline ll re(){
    ll x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= f;
    return x;
}
inline void write(ll x){
	if (x < 0){putchar('-'); x = -x;}
	ull y = 10, len = 1;
	while (y <= x){y *= 10; len++;}
	while (len--){y /= 10; putchar(x / y + 48); x %= y;}
}
void top_sort(){
	PQL q = PQL ();
	int i;
	rep(i,1,n,1) if(!in[i]) q.push(i);
	while(!q.empty()){
		int u = q.top(); ans.push(u); q.pop();
		for(i = head[u];i;i = e[i].next){
			int v = e[i].v; in[v]--;
			if(!in[v]) q.push(v);
		}
	}
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	n = re(),m = re();
	int i,u,v;
	rep(i,1,m,1){
		u = re(),v = re();
		add(v,u); in[u]++;
	}
	top_sort();
	rep(i,1,n,1) if(in[i]){puts("-1"); return 0;}
	while(!ans.empty()){
		write(ans.top());
		ans.pop();
		putchar(' ');
	} 
	return 0;
} 

运行结果:

三、推荐几个题目

P1038P1137P1347P1807P1983
其他题目到洛谷上搜索吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值