【Luogu】 P1954 [NOI2010] 航空管制

该文描述了一个涉及航班调度的问题,其中包含两类限制条件。作者提出将问题转化为拓扑序问题,并通过反向操作处理限制。第一类限制通过贪心策略解决,第二类限制结合反向拓扑序。文章提供了算法实现,包括两部分:一是找到满足所有条件的航班顺序,二是找出最早可添加的航班。时间复杂度分别对应为O(n)和O(n^2)。
摘要由CSDN通过智能技术生成

题目链接

点击打开链接

题目解法

我们发现第二类限制明显是一个拓扑序问题
第一类限制是一个贪心,如果只考虑第二个限制的话,可以把时间从后往前扫,航班只要填一个能填的就行,这样明显是对的

考虑将二者结合,我们可以把操作都反向,将第一类限制变为 航班为 i i i 的航班起飞序号不得小于 k i k_i ki,同时建立原拓扑图的反向拓扑图

考虑第一问,只要按照之前只考虑第一类性质的贪心,在加上拓扑序都必须满足就可以了
如果要证明的话可以考虑如果一个顺序是合法的,且从后面抽一些航班到前面也是合法的,那么后面抽一些航班到前面,其他的按照原序也必定是合法的
时间复杂度 O ( n ) O(n) O(n)

考虑第二问
枚举每个航班
做法与第一问相同,只要特判不能走到当前的航班即可
如果当前时间没有航班可以加了,那么这个时间就是答案
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;
const int N(2100),M(10100);
int n,m,k[N],ans[N],rudu[N];
int e[M],ne[M],h[N],idx;
vector<int> vec[N];
int wq[N],top;
bool cov1[N],cov2[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void add(int x,int y){
	e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) k[i]=read(),k[i]=n-k[i]+1;
	for(int i=1;i<=n;i++) vec[k[i]].push_back(i);
	memset(h,-1,sizeof(h)); 
	for(int i=1,a,b;i<=m;i++) a=read(),b=read(),add(b,a),rudu[a]++;
	for(int i=1;i<=n;i++) if(!rudu[i]) cov2[i]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<vec[i].size();j++){
			int x=vec[i][j];cov1[x]=1;
			if(cov2[x]) wq[++top]=x;
		}
		ans[i]=wq[top--];
		for(int j=h[ans[i]];~j;j=ne[j]){
			int x=e[j];rudu[x]--;
			if(!rudu[x]){
				cov2[x]=1;
				if(cov1[x]) wq[++top]=x;
			}
		}
	} 
	for(int i=n;i;i--) printf("%d ",ans[i]);
	puts("");
	for(int cur=1;cur<=n;cur++){
		for(int i=1;i<=n;i++) rudu[i]=cov1[i]=cov2[i]=0;
		for(int i=1;i<=n;i++) for(int j=h[i];~j;j=ne[j]) rudu[e[j]]++;
		for(int i=1;i<=n;i++) if(!rudu[i]&&i!=cur) cov2[i]=1;
		top=0;
		for(int i=1;i<=n;i++){
			for(int j=0;j<vec[i].size();j++){
				int x=vec[i][j];cov1[x]=1;
				if(cov2[x]&&x!=cur) wq[++top]=x;
			}
			if(!top){ printf("%d ",n-i+1);break;}
			int p=wq[top--];
			for(int j=h[p];~j;j=ne[j]){
				int x=e[j];rudu[x]--;
				if(!rudu[x]){
					cov2[x]=1;
					if(cov1[x]&&x!=cur) wq[++top]=x;
				}
			}
		} 
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值