拓扑排序(习题笔记 思路整理)之一

​​​​​​1.P4017 最大食物链计数

确实比较板子,但是考验思维,我愿称之为思维拓扑排序

作为拓扑的第一题,确实没想到要进行思维处理

这里考虑用一种dp的思维方式(?

多引入两个变量,一个记录出度,一个记录每一个点的种类数量,这样就可以一直满足不同方向过来所产生的种类数量

出度的缘由是这里需要绝对的两端(我开始以为是大于二的食物链都行,那样的话就每一次操作都+1就好)

贴代码(不要忘记取模,不然只有两个点捏)

// Problem: 
//     P4017 最大食物链计数
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4017
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=5010;
vector<int> e[N];
int ans;
int din[N];//入度
int oin[N];//出度
int n,m;
int f[N];//每个点的数量

void toposort(){
    queue<int> q;
    for(int i=1;i<=n;++i){
    	if(din[i]==0) q.push(i),f[i]=1;
    }	
    while(q.size()){
    	auto k=q.front();q.pop();
    	for(auto t:e[k]){
    		if(--din[t]==0) q.push(t);
    		f[t]=(f[k]+f[t])%80112002;
    	}
    }
    for(int i=1;i<=n;++i){
    	if(oin[i]==0) ans=(f[i]+ans)%80112002;
    }
}

int main(){
    cin>>n>>m;
    while(m--){
    	int a,b;cin>>a>>b;
    	e[a].push_back(b);
    	++din[b];
    	++oin[a];
    }	
    toposort();
    cout<<ans%80112002<<endl;
	return 0;
}

2.P2712 摄像头

这道题比较板子,在操作的时候,如果pop就ans--即可,有几个坑点

输出yes 摄像头的范围  摄像头所能照到的位置不一定会存在摄像头(怎么会有题卡这种鬼地方)

// Problem: 
//     P2712 摄像头
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2712
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=505;
vector<int> e[N];
int ans=0;
int din[N];
bool check[N];
vector<int> kkk(N);

void toposort(){
	queue<int> q;
	for(int i=1;i<=ans;++i){
		if(din[kkk[i]]==0) q.push(kkk[i]);
	}
	while(q.size()){
		auto t=q.front();
		q.pop();
		//cout<<t<<endl;
		//cout<<ans<<endl;
		//cout<<endl;
		if(check[t]) ans--;
		for(auto x:e[t]){
			if(--din[x]==0) q.push(x);
		}
	}
}


int main(){
	int n;cin>>n;
    ans=n;
    int cnt=0;
	while(n--){
		int a,b;cin>>a>>b;
		if(!check[a]) kkk[++cnt]=a,check[a]=true;
		while(b--){
			int k;cin>>k;
			e[a].push_back(k);
			din[k]++;
		}
	}
	toposort();
	if(ans==0) cout<<"YES"<<endl;
	else cout<<ans<<endl;
	return 0;
}

3.P1137 旅行计划

这道题比上一道题要简单不少,与第一题思路相同,但这一题的想法就是维护一个max就好了,我其实没有太看懂提,但是这么操作不管有几个起始点,都可以获得最大的值(看来自各个方向哪个大就选哪个方向)

// Problem: 
//     P1137 旅行计划
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1137
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int N=1e5+10;
vector<int> e[N];
int din[N];
int f[N];
int n,m;

void toposort(){
	queue<int> q;
	for(int i=1;i<=n;++i){
		if(din[i]==0) q.push(i);
		f[i]=1;
	}
	while(q.size()){
		auto t=q.front();
		q.pop();
		for(auto x:e[t]){
			if(--din[x]==0) q.push(x);
			f[x]=max(f[t]+1,f[x]);
		}
	}
}
int main(){
	cin>>n>>m;
	while(m--){
		int a,b;cin>>a>>b;
		e[a].push_back(b);
		din[b]++;
	}
	toposort();
	for(int i=1;i<=n;++i) cout<<f[i]<<endl;
	
	
	return 0;
}

4.P1960 郁闷的记者

这道题难过的令人无语,调试了一整天才发现是读错题了。这题面真的阴间.......

我的想法是开两个堆分别维护最大字典序和最小字典序,如果答案不同说明有不同情况(极端情况)

贴代码

// Problem: 
//     P1960 郁闷的记者
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1960
// Memory Limit: 500 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N=1e4+10;
int cnt1[N];//记录名次
int cnt2[N];
int n,m;
vector<int> e[N];
int din1[N];
int din2[N];

void toposort(){
	priority_queue<int> a;
	priority_queue<int,vector<int>,greater<int>> b;
	for(int i=1;i<=n;++i){
		//cout<<din1[i]<<endl;
		 if(din1[i]==0){
		 	 a.push(i);b.push(i);//cout<<i<<endl;
		 }
	}
	int now=0;
	while(a.size()){
		auto t=a.top();
		a.pop();
		cnt1[t]=++now;
		//cout<<t<<endl;
		//cout<<t<<endl;
		for(auto x:e[t]){
			if(--din1[x]==0) a.push(x);
		}
	}
	now=0;
	while(b.size()){
		auto t=b.top();
		b.pop();
		cnt2[t]=++now;
		cout<<t<<endl;
		for(auto x:e[t]){
			if(--din2[x]==0) b.push(x);
		}
	}
}

int main(){
	cin>>n>>m;
	while(m--){
		int a,b;cin>>a>>b;
		e[a].push_back(b);
		din1[b]++;din2[b]++;
	}
	toposort();
	//for(int i=1;i<=n;++i) cout<<cnt2[i]<<endl;
	for(int i=1;i<=n;++i){
		if(cnt1[i]!=cnt2[i]){
			cout<<1<<endl;
			return 0;
		}
	}
	cout<<0<<endl;
	return 0;
}

5.P6145 [USACO20FEB] Timeline G

一遍就AC,想的时间也是最短的。

但是这道题确实比较难想,要是没跟我说这是拓扑排序我不一定能想到。这道题比较隐晦,我的思路是一个类似dp的操作(每一次都更新最大),也就是每一次都要更新到最大。

这里使用拓扑排序的原因,我总结为:每一个的结局时间都是不确定的,要先确定前面的才能确定后面的,这个过程就是一个拓扑序。

在这个拓扑序遍历的时候,对每一次进行更新(对某个点有影响的所有点(已经没有入度了)继续更新),语言无力,请看代码。(我没看懂题解区说的递推是啥

// Problem: 
//     P6145 [USACO20FEB] Timeline G
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6145
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n,m,s;
const int N=1e5+10;
int f[N];
vector<pair<int,int>> e[N];
int din[N];

void toposort(){
	queue<int> q;
	for(int i=1;i<=n;++i){
		if(din[i]==0) q.push(i);
	}
	while(q.size()){
		auto t=q.front();
		q.pop();
		for(auto x:e[t]){
			int a=x.first,b=x.second;
			f[a]=max(f[a],f[t]+b);//每次都更新一下
			if(--din[a]==0) q.push(a);
		}
	}
}

int main(){
	cin>>n>>m>>s;
	for(int i=1;i<=n;++i) cin>>f[i];
	while(s--){
		int a,b,c;cin>>a>>b>>c;
		e[a].push_back({b,c});
		din[b]++;
	}
	toposort();
	for(int i=1;i<=n;++i) cout<<f[i]<<endl;
	
	
	return 0;
}

6.P1807 最长路

这道题有点坑,有不少遭点,一开始我以为可以直接copy上一题代码(还是太想去睡觉了),结果发现必须是从1到n的路径。

这里有几个坑点

1.从1到n  且题目已经告诉你路径一定是从小节点到大节点的,枉我想了这么久的环

2.这里的权值可以是负数,所以应该把2-n的所有编程-1e9

3.由于指定了起始位置,还会存留一下入度为0的值,对后面的数字造成不良影响,对此,我们可以把这些先一步捞出来处理一下就好,见代码

好像是dp的方法吧,话说感觉越来越板了,拓扑都是这样的题目,每道题都要结合一点dp思想来着

上代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n,m,s;
const int N=2000;
const int M=5e4+10;//数据不要开错了
int f[N];
vector<pair<int,int>> e[M];
int din[N];
bool check;

void toposort(){
	queue<int> q;
	for(int i=2;i<=n;++i){
		if(din[i]==0) q.push(i);
		f[i]=-1e9;
	}
	while(q.size()){//多一个处理,减少这些不会入队的值对入队的影响
		auto t=q.front();
		q.pop();
		for(auto x:e[t]){
			int a=x.first;
			if(--din[a]==0) q.push(a);
		}
	}
	q.push(1);
	while(q.size()){
		auto t=q.front();
		q.pop();
		if(t==n) check=true;
		for(auto x:e[t]){
			int a=x.first,b=x.second;
			f[a]=max(f[a],f[t]+b);
			if(--din[a]==0) q.push(a);
		}
	}
}

int main(){
	cin>>n>>m;
	while(m--){
		int a,b,c;cin>>a>>b>>c;
		e[a].push_back({b,c});
		din[b]++;
	}
	toposort();
	if(check) cout<<f[n]<<endl;
	else cout<<-1<<endl;
	
	return 0;
}

7.P1113 杂务

这道题小细节比较多,我调试了一个钟,但是思路与前面又很大的相似之处,其实好像又是dp思想,难度不大,直接贴代码

// Problem: 
//     P1113 杂务
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1113
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int n;
const int N=1e4+10;
int din[N];
int f[N];
int now[N];
vector<int> e[N];
int ans=0;

void toposort(){
	queue<int> q;
	for(int i=1;i<=n;++i){
		if(din[i]==0) q.push(i);
	}
	while(q.size()){
		auto t=q.front();
		q.pop();
		ans=max(ans,now[t]);
		for(auto x:e[t]){
			now[x]=max(now[x],now[t]+f[x]);
			//cout<<now[x]<<endl;
			//cout<<1<<endl;
			if(--din[x]==0) q.push(x);
		}
	}
}

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>i;
		int k;
		cin>>f[i];
		now[i]=f[i];
		while(cin>>k){
			if(k==0) break;
			else{
				e[k].push_back(i);din[i]++;
				//cout<<i<<' '<<din[i]<<endl;
			}
		}
	}
	//for(int i=1;i<=n;++i) cout<<din[i]<<endl;
	toposort();
	cout<<ans<<endl;
	return 0;
}

8.P1983 [NOIP2013 普及组] 车站分级

绿题,比较考验思维,我在看题解前,大致想到的思路是,相邻的数据中,找不同的从而进行比较(没有考虑到,点的等级关系只在起点到终点中有关,从而想假了)

大致想法是,在一段中,出现的点一定比未出现的等级高,因为比出现的点等级更高的点一定会出现(有点绕)

此外,用一点dp思想,处理一下等级就好(前几道题就在干这事)

代码如下

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n,m;
const int N=1100;
vector<int> e[N];
bool check[N][N];
int f[N];
int din[N];

void toposort(){
	queue<int> q;
	for(int i=1;i<=n;++i){
		if(din[i]==0) q.push(i);
	}
	int maxn=0;
	while(q.size()){
		auto t=q.front();q.pop();
		maxn=max(maxn,f[t]);
		for(auto x:e[t]){
			if(--din[x]==0) q.push(x);
			f[x]=max(f[t]+1,f[x]);
		}
	}
	cout<<maxn+1<<endl;
}

int main(){
    cin>>n>>m;
    while(m--){
    	int x;cin>>x;
    	vector<bool> a(n+1);
    	vector<int> b(x+2);
    	for(int i=1;i<=x;++i){
    		cin>>b[i];a[b[i]]=1;
    	}
    	for(int j=b[1];j<=b[x];++j){
    		if(a[j]) continue;
    		for(int k=1;k<=x;++k){
    			if(!check[b[k]][j]) e[b[k]].push_back(j),check[b[k]][j]=1,din[j]++;
    		}
    	}
    }
	toposort();
	
	return 0;
}

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值