拓扑排序
概念
“对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。”——《百度百科》
上面这一段已经说得很清楚了
对于一个有向图,我们需要确定他节点的顺序,使得它的所有出边指向他后面的节点
显然,一个有环的图是无法进行拓扑排序的
我们把可以进行拓扑排序的图成为一张拓扑图
显然拓扑图就得 DAG (有向无环图)
HOW?
可以运用 bfs
- 首先将所有入度为零的点入队
- 若队列非空
- 取出对头结点,并枚举它的每个出边
- 对每个出边的入度减一
- 如果这个节点入读变成了零,就把他压入队列
说白了就是一个删边、入队的过程,显然一个 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;
}