[CSP-S 2021] 廊桥分配 题解

写篇题解来纪念我炸掉的CSP

唯一会做的题代码写挂了(痛苦面具

思路

我看到这道题第一眼想到的是线段树,感觉可以用线段树维护飞机入站到出战的这段时间,想了半天想不到代码怎么写

国内机场与国外机场要分开计算

突然发现可以用一个优先队列来维护飞机出站的时间,给每架飞机按入站时间排好序后可以从小到大依次让飞机入站,并比较此时是否有飞机出站,有就把队首元素弹出。

40 pts

可以暴力枚举每一种国内机场和国际机场廊桥的分配方案。

代码如下:

#include <bits/stdc++.h>
#include <functional>
#define N 100010
using namespace std;

struct node {
	int a, b;
} fa[N], fc[N];//分别为国外机场航班和国内机场航班
int n, m1, m2, _ans;

bool cmp(node a, node b) {
	return a.a < b.a;
}

void check(int ans) {//ans表示国内机场分配的廊桥数
	priority_queue <int, vector<int>, greater<int> > q;
	int res = 0;
	for (int i = 1; i <= m1; i++) {
		int s = fc[i].a;
		while (!q.empty() && s > q.top()) {
			q.pop();//弹出已经出站的飞机
		}
		if (q.size() < ans) {
			q.push(fc[i].b);//如果有空余的廊桥就加入廊桥
			res++;//答案+1
		}
	}

	while (!q.empty())
		q.pop();//清空队列元素

	for (int i = 1; i <= m2; i++) {
		int s = fa[i].a;
		while (!q.empty() && s > q.top()) {
			q.pop();
		}
		if (q.size() < n - ans) {//n-ans表示国外机场分配的廊桥数
			q.push(fa[i].b);//同上
			res++;
		}
	}
	_ans = max(_ans, res);
	return ;
}

int main() {
	scanf("%d%d%d", &n, &m1, &m2);
	for (int i = 1; i <= m1; i++) {
		scanf("%d%d", &fc[i].a, &fc[i].b);
	}
	for (int i = 1; i <= m2; i++) {
		scanf("%d%d", &fa[i].a, &fa[i].b);
	}
	sort(fc + 1, fc + 1 + m1, cmp);
	sort(fa + 1, fa + 1 + m2, cmp);//按入站时间排序

	for (int i = 0; i <= n; i++) {
		check(i);//暴力枚举统计答案
	}

	printf("%d", _ans);
	return 0;
}

复杂度为O(nm logm)

100pts

以上做法的复杂度太高,这时候想有没有什么办法可以一遍就统计出所有分配方案的答案,然后统计答案。

统计答案的代码:

for (int i = 0; i <= n; i++) {
	if (ans1[i] + ans2[n - i] > maxn) {
		maxn = ans1[i] + ans2[n - i];
	}
}

如果给每个廊桥都编上号,每次飞机入站都选择空闲的廊桥中编号最小的哪一个。

这样可以排除分配方案的影响。

用一个优先队列来维护廊桥编号,一开始将所有廊桥入队。

对于每一架飞机可以用一个pair分别存储飞机出站的时间以及停靠的廊桥的编号。

用优先队列来维护pair

每次飞机入站时先比较有没有应该出站的飞机,如果有就把他们从队列中弹出,并把对应的廊桥编号再加入优先队列中。

然后飞机选择编号最小的廊桥停靠。并把对应的廊桥的ans加一。

这样就能快速的统计出每个廊桥产生的贡献,再把所有编号比它小的廊桥产生的贡献加起来,用前缀和数组来维护。

最后用上面的代码统计一遍答案就好了。

代码如下

void check() {
	priority_queue<int, vector<int>, greater<int> > q;//维护廊桥编号
	priority_queue<P, vector<P>, greater<P> > p;//维护飞机出站时间和停靠的廊桥的编号
	for (int i = 1; i <= n; i++) {
		q.push(i);//一开始所有廊桥都处于空闲状态,把所有廊桥加入队列。
	}
	for (int i = 1; i <= m1; i++) {
		int s = fc[i].a;//该架飞机入站时间
		while (!p.empty() && s > p.top().first) {
			//弹出已经飞走的飞机并把它所停课的廊桥加入队列
			q.push(p.top().second);
			p.pop();
		}
		if (!q.empty()) {
			int id = q.top();
			q.pop();//取编号最小的廊桥停靠
			ans1[id]++;//该编号的廊桥产生的贡献加一
			p.push(P(fc[i].b, id));
		}
	}
	while (!q.empty())
		q.pop();
	while (!p.empty())
		p.pop();
	//清空队列
	for (int i = 1; i <= n; i++) {
		q.push(i);
	}
	//同上
	for (int i = 1; i <= m2; i++) {
		int s = fa[i].a;
		while (!p.empty() && s > p.top().first) {
			q.push(p.top().second);
			p.pop();
		}
		if (!q.empty()) {
			int id = q.top();
			q.pop();
			ans2[id]++;
			p.push(P(fa[i].b, id));
		}
	}
	for (int i = 1; i <= n; i++) {
		ans1[i] += ans1[i - 1];
		ans2[i] += ans2[i - 1];
	}
	//前缀和数组维护每种分配方案产生的答案
	return;
}

复杂度 O(nlogn)

Code

#include <bits/stdc++.h>
#include <cstdio>
#include <functional>
#define P pair <int,int>
using namespace std;

struct node {
	int a, b;
} fc[100010], fa[100010];

bool cmp(node a, node b) {
	return a.a < b.a;
}
int n, m1, m2, maxn = -1, ans1[100010], ans2[100010];

void check() {
	priority_queue<int, vector<int>, greater<int> > q;//维护廊桥编号
	priority_queue<P, vector<P>, greater<P> > p;//维护飞机出站时间和停靠的廊桥的编号
	for (int i = 1; i <= n; i++) {
		q.push(i);//一开始所有廊桥都处于空闲状态,把所有廊桥加入队列。
	}
	for (int i = 1; i <= m1; i++) {
		int s = fc[i].a;//该架飞机入站时间
		while (!p.empty() && s > p.top().first) {
			//弹出已经飞走的飞机并把它所停课的廊桥加入队列
			q.push(p.top().second);
			p.pop();
		}
		if (!q.empty()) {
			int id = q.top();
			q.pop();//取编号最小的廊桥停靠
			ans1[id]++;//该编号的廊桥产生的贡献加一
			p.push(P(fc[i].b, id));
		}
	}
	while (!q.empty())
		q.pop();
	while (!p.empty())
		p.pop();
	//清空队列
	for (int i = 1; i <= n; i++) {
		q.push(i);
	}
	//同上
	for (int i = 1; i <= m2; i++) {
		int s = fa[i].a;
		while (!p.empty() && s > p.top().first) {
			q.push(p.top().second);
			p.pop();
		}
		if (!q.empty()) {
			int id = q.top();
			q.pop();
			ans2[id]++;
			p.push(P(fa[i].b, id));
		}
	}
	for (int i = 1; i <= n; i++) {
		ans1[i] += ans1[i - 1];
		ans2[i] += ans2[i - 1];
	}
	//前缀和数组维护每种分配方案产生的答案
	return;
}

int main() {
	scanf("%d%d%d", &n, &m1, &m2);
	for (int i = 1; i <= m1; i++) {
		scanf("%d%d", &fc[i].a, &fc[i].b);
	}
	for (int i = 1; i <= m2; i++) {
		scanf("%d%d", &fa[i].a, &fa[i].b);
	}
	sort(fc + 1, fc + 1 + m1, cmp);
	sort(fa + 1, fa + 1 + m2, cmp);
	check();
	//统计答案
	for (int i = 0; i <= n; i++) {
		if (ans1[i] + ans2[n - i] > maxn) {
			maxn = ans1[i] + ans2[n - i];
		}
	}
	printf("%d", maxn);
	return 0;
}

上一篇:二叉树的层平均值python(leetcode637)


下一篇:C++容器详解之stack