拓扑排序

有向图的拓扑序列

注意:如果拓扑排序不能遍历到所有点,说明拓扑排序不存在

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII; 
const int N=1e5+5,M=N*2;
int n,m;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
int d[N];
vector<int> vc;
bool check(){
	queue<int> q;
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(!d[i]){
			q.push(i);
			cnt++;
			vc.push_back(i);
		}
	}
	while(!q.empty()){
		int t=q.front();q.pop();
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			d[j]--;
			if(!d[j]){
				q.push(j);
				cnt++;
				vc.push_back(j);
			}
		}
	}
	return cnt==n;
}
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		add(x,y);
		d[y]++;
	}
	if(check()){
		for(auto t:vc){
			cout<<t<<" ";
		}
	}else{
		cout<<-1;
	}
} 

构造有向无环图

3696. 构造有向无环图 - AcWing题库

题意:给定若干有向边和无向边,为无向边指定方向,使其成为有向无环图

        按照已经存在的有向边进行拓扑序遍历,遍历的过程中记录每个点被遍历的次序。如果拓扑排序能够遍历到所有的点,则说明可以构成有向无环图。对于每个无向边的两个端点,其方向为由次序考前的点指向次序靠后的点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII; 
const int N=2e5+5,M=N*2;
int n,m;
int h[N],e[M],ne[M],idx;
int d[N];
void add(int a,int b){
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
struct edge{
	int a,b;
}edge[M];
int s[N];
//拓扑序遍历,记录每个点的遍历次序 
bool check(){
	queue<int> q;
	for(int i=0;i<=n;i++){
		s[i]=0;
	}
	int k=0;
	for(int i=1;i<=n;i++){
		if(!d[i]){
			q.push(i);
			s[i]=k++;
		}
	}
	while(!q.empty()){
		int t=q.front();q.pop();
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			d[j]--;
			if(!d[j]){
				q.push(j);
				s[j]=k++;
			}
		}
	}
	return k==n;//不能遍历所有点则无法构成有向无环图 
}
int main(){
	int t;cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i=0;i<=n;i++) d[i]=0;
		for(int i=0;i<=n;i++) h[i]=-1;
		idx=0;
		for(int i=1;i<=m;i++){
			int t,x,y;cin>>t>>x>>y;
			if(t){
				add(x,y);
				d[y]++;
			}
			edge[i]={x,y};
		}
		if(check()){
			cout<<"YES"<<endl;
			for(int i=1;i<=m;i++){
				int x=edge[i].a;
				int y=edge[i].b;
				//遍历次序在前的指向在后的 
				if(s[x]<s[y]){
					cout<<x<<" "<<y<<endl;
				}else{
					cout<<y<<" "<<x<<endl;
				}
			}
		}else{
			cout<<"NO"<<endl;
		}	
	}
} 

车站分级

456. 车站分级 - AcWing

        计算当前线路中最小的级别,整条线路中所有大于这个级别的都必须停靠,所有未停靠的站点的级别一定小于这个级别,记未停靠(级别低)的车站为A,停靠(级别高)的车站为B,则有B>=A+1;又因级别>=1,则A>=1,可以转化为差分约束问题,从所有的A向所有的B连一条权值为1的有向边,从0向所有的A连一条权值为1的有向边(直接给dist数组赋值为1代替) 然后跑最长路求最小值,得到的参数中的最大值即为答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII; 
const int N=2e3+5,M=N*N;
int n,m;
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c){
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx++;
}
int d[N];
vector<int> vc;
void check(){
	queue<int> q;
	for(int i=1;i<=n+m;i++){
		if(!d[i]){
			q.push(i);
		}
	}
	while(!q.empty()){
		int t=q.front();q.pop();
		vc.push_back(t);
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			d[j]--;
			if(!d[j]){
				q.push(j);
			}
		}
	}
}
bool stop[N];
int dis[N];
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++){
		memset(stop,false,sizeof stop);
		int st=n,ed=1;
		int t;cin>>t;
		while(t--){
			int x;cin>>x;
			st=min(st,x);
			ed=max(ed,x);
			stop[x]=true;
		}
		int s=n+i;//虚拟点 
		//停靠的车站的等级一定大于不停靠的车站的等级 
		//对于始发站到终点站的所有停靠点
		//如果停靠,属于右侧点,从虚拟点向该点连1边
		//如果未停靠,属于左侧点,从该点向虚拟点连0边 
		for(int j=st;j<=ed;j++){
			if(stop[j]){
				add(s,j,1);
				d[j]++;
			}else{
				add(j,s,0);
				d[s]++;
			}
		}
	}
	check();
	for(int i=1;i<=n;i++){
		dis[i]=1;
	}
	//根据拓扑排序的顺序,解差分约束
	//跑最长路,得最小值 
	for(auto t:vc){
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			dis[j]=max(dis[j],dis[t]+w[i]);
		}
	}
	int res=0;
	//车站等级最小值的最大值为 最少的级别数 
	for(int i=1;i<=n;i++){
		res=max(res,dis[i]);
	}
	cout<<res;
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vic.GoodLuck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值