图论之拓扑排序

基本概念

  • 人们有时要做很多事情,这些事情之间又有依赖关系或者说有优先级之分,只有优先级高的事情做完之后才能去做优先级低的事,比如穿衣服的先后,课程学习的顺序,安排客人的座位等。

  • 例如:存在事情a,b,c,d,其中a的优先级最高,b,c的优先级其次,d的优先级最低,则abcd或者acbd就是两种排序,我们把事情变成点,先后关系变成边,则在图中求一个可行的排序,就是拓扑排序

  • 一个图能进行拓扑排序的充要条件是它是一个有向无环图(DAG)

算法学习

图的入度与出度

  • 出度:以点u为起点的边的数量
  • 入度:以点v为终点的边的数量
  • 一个点的入度和出度的数量间接体现了这个点的优先级,当一个点不存在入度的时候,则该点就是优先级最高的点

BFS拓扑排序(模板)

  • 原理:无前驱的顶点优先拓扑排序
  • 方法:每次将入度为0的点加入队列,作为起点,弹出该点,去遍历该点的所有邻居点,将邻居点的入度减1,如果该邻居点的入度减少到0,则将其加入队列,以此类推
  • 判断拓扑排序无解的方法:当队列空的时候仍存在点还未进入过队列,则这些点的入度都不为0,则说明图不是DAG,不存在拓扑排序
void bfs(){
	for(int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty()){
		int u=q.top();
		cout<<u<<" "; 
		for(int i=vex[u];i;i=e[i].next){
			int v=e[i].v;
			in[v]--;
			if(!in[v])q.push(v);
		}
		q.pop();
	}
}
int main(){
	int m,u,v;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		add(u,v);
		in[v]++;
	}
	bfs();
	return 0;
} 

字典序最小的拓扑排序

  • 即,若存在若干个优先级相同的点,优先输出点编号较小的点。只需要优先队列(小根堆)代替普通队列即可。
priority_queue<int,vector<int>,greater<int> >q;
void bfs(){
	for(int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty()){
		int u=q.top();
		cout<<u<<" "; 
		q.pop();
		for(int i=vex[u];i;i=e[i].next){
			int v=e[i].v;
			in[v]--;
			if(!in[v])q.push(v);
		}
	}
}
int main(){
	int m,u,v;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		add(u,v);
		in[v]++;
	}
	bfs();
	return 0;
} 

小值尽可能先做的拓扑排序

  • 小值尽可能先做等价于大值尽可能后做,反向建图跑字典序大的拓扑序即可得到逆序的答案。
priority_queue<int>q;
void bfs(){
	for(int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty()){
		int u=q.top();
		ans[++top]=u; 
		q.pop();
		for(int i=vex[u];i;i=e[i].next){
			int v=e[i].v;
			in[v]--;
			if(!in[v])q.push(v);
		}
	}
	for(int i=top;i>=1;i--)cout<<ans[i]<<" ";
}
int main(){
	int m,u,v;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		add(u,v);
		in[u]++;
	}
	bfs();
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值