第一节——拓扑排序
一、前置知识
本文需要用到图、队列和邻接表的相关知识,不会的可以看看别人的,我找了几篇:
图
队列
链式前向星
二、拓扑排序
简介
拓扑排序是对一个有向图构造拓扑序列,解决工程是否能顺利进行的问题。构造时有 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;
}
运行结果: