Loading

luogu P1954 [NOI2010] 航空管制

题目大意

一个机场有\(n\)个飞机要起飞
一架飞机\(a\)起飞有两个限制:

  1. 起飞时间不晚于\(k_i\)
  2. \(b\)之前起飞

给定\(a\),\(b\),\(k\)且保证题目有解

  1. 求一个合法的起飞顺序
  2. 求每一架飞机\(i\)在所有可行方案中的最早起飞时间

解法

Q1

既然保证题目有解,那么可以直接建立拓扑图
按照拓扑排序的顺序,将入度为零的点入堆
每次取出堆中\(k\)值最大的点,所构成的点的顺序就是答案的倒序
时间复杂度\(O(n\log n)\)

证明可以使用反证法
若我们取出的\(k\)值最大的点的编号为\(b_i\) ,且\(k\)\(k_i\) ,必须在\(a_i\)后起飞
容易发现一定满足\(a_i<b_i\) ,所以考虑\(k_i\)不满足题目
我们已经取出的序列为\(k_i\) ,\(k_{i+1}\) ,\(k_{i+2}\) ,... ,\(k_{n-1}\) ,\(k_{n}\)
其中的每一个都应该是当时的最大\(k_i\)
假设答案序列为\(ans\)且与当前序列不同,就一定有某个\(ans_i<ans_{i+1}\) ,将其互换一定满足答案

Q2

注意到\(n\)范围很小,可以支持\(O(n^2\log n)\)做法
和刚才想法一样,但是我们考虑枚举每一个点\(u\)
如果想让\(u\)在正序下最先遍历,就是让\(u\)在倒序最后遍历
在拓扑排序时若遍历到\(u\)就跳过,直到遍历到第\(num+1\)个节点不满足时间限制
(即一共有\(num\)个在\(u\)前满足时间限制)
所以\(u\)的答案就是\(n-num\)
时间复杂度\(O(n^2\log n)\)

证明:
若遍历到第\(u\)个点时\(v\)不符合题目要求
\(v\)一定为剩下所有点中\(k\)最大的点
\(k_v<n-num\)则剩下所有点都不符合题目条件,只能取\(u\)作为遍历的下一个点
由于我们依旧取\(\max k_i\) ,所以最优性同上,遍历\(u\)\(num-1\)一定存在答案,否则题设不成立(反证)

总结

时间复杂度\(O(n^2\log n)\)
空间复杂度\(O(n)\)
足以通过本题

代码

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>

#define x first
#define y second

using namespace std;

typedef pair<int,int> PII;

const int N = 2e3+10 , M = 1e4+10;

int n , m;
int k[N];
int in[N] , backup[N];
int h[N] , ne[M] , e[M] , idx;
priority_queue<PII> q;

void add(int a , int b) {
	e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}

void slove1() {
	for(int i = 1 ; i <= n ; i ++)
		if(!in[i]) q.push({k[i] , i});
	
	vector<int> ans;
	while(q.size()) {
		PII t = q.top(); q.pop();
		ans.push_back(t.y);
		
		for(int i = h[t.y] ; ~i ; i = ne[i]) {
			int j = e[i];
			in[j] --;
			if(!in[j]) {
				q.push({k[j] , j});
			}
		}
	}
	
	for(auto it = ans.rbegin() ; it != ans.rend() ; it ++)
		printf("%d " , *it);
	puts("");
	return;
}

void slove2() {
	vector<int> ans;
	for(int i = 1 ; i <= n ; i ++) {
		memcpy(in , backup , sizeof in);
		
		for(int j = 1 ; j <= n ; j ++)
			if(!in[j]) q.push({k[j],j});
		
		int num = 0;
		while(q.size()) {
			PII t = q.top(); q.pop();
			if(n-num > t.x) break;
			if(t.y == i) continue;
			
			num ++;
			for(int i = h[t.y] ; ~i ; i = ne[i]) {
				int j = e[i];
				in[j] --;
				if(in[j] == 0) q.push({k[j],j});
			}
		}
		ans.push_back(n-num);
		while(q.size()) q.pop();
	}
	for(auto i : ans) printf("%d " , i);
	puts("");
	return;
}

int main() {
	scanf("%d%d" , &n , &m);
	for(int i = 1 ; i <= n ; i ++)
		scanf("%d" , &k[i]);
	
	memset(h , -1 , sizeof h);
	for(int i = 1 ; i <= m ; i ++) {
		int a , b; scanf("%d%d" , &a , &b);
		add(b , a);
		in[a] ++;
	}
	memcpy(backup , in , sizeof in);
	slove1();
	
	slove2();
	return 0;
}
posted @ 2025-09-07 20:30  lyr2023  阅读(2)  评论(0)    收藏  举报