luogu P1954 [NOI2010] 航空管制
题目大意
一个机场有\(n\)个飞机要起飞
一架飞机\(a\)起飞有两个限制:
- 起飞时间不晚于\(k_i\)
- 在\(b\)之前起飞
给定\(a\),\(b\),\(k\)且保证题目有解
- 求一个合法的起飞顺序
- 求每一架飞机\(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;
}