[CSP-S 2021] 廊橋分配 題解

Dregen_Yor發表於2021-10-31

寫篇題解來紀念我炸掉的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;
}

相關文章