[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;
}
[CSP-S 2021] 廊桥分配 题解的更多相关文章
- [CSP-S2021] 廊桥分配
		链接: P7913 题意: 有 \(m_1\) 架飞机和 \(m_2\) 架飞机停在两个机场,每架飞机有到达和离开的时间,要将 \(n\) 个廊桥分给两个机场,每个廊桥同一时刻只能停一架飞机,需要最大 ... 
- POJ 3694 Network(并查集缩点 + 朴素的LCA + 无向图求桥)题解
		题意:给你一个无向图,有q次操作,每次连接两个点,问你每次操作后有几个桥 思路:我们先用tarjan求出所有的桥,同时我们可以用并查集缩点,fa表示缩点后的编号,还要记录每个节点父节点pre.我们知道 ... 
- UVA796 Critical Links(求桥) 题解
		题意:求桥 思路:求桥的条件是:(u,v)是父子边时 low[v]>dfn[u] 所以我们要解决的问题是怎么判断u,v是父子边(也叫树枝边).我们在进行dfs的时候,要加入一个fa表示当前进行搜 ... 
- 上午小测3 T1 括号序列 && luogu P5658 [CSP/S 2019 D1T2] 括号树 题解
		前 言: 一直很想写这道括号树..毕竟是在去年折磨了我4个小时的题.... 上午小测3 T1 括号序列 前言: 原来这题是个dp啊...这几天出了好几道dp,我都没看出来,我竟然折磨菜. 考试的时候先 ... 
- [CSP模拟测试43、44]题解
		状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ... 
- UOJ 2021 NOI Day2 部分题解
		获奖名单 题目传送门 Solution 不难看出,若我们单个 \(x\) 连 \((0,x),(x,0)\),两个连 \((x,y),(y,x)\) ,除去中间过对称轴的一个两个组,就是找很多个欧拉回 ... 
- USACO 2021 Contest 1 Bronze 题解
		蒟蒻第一次打 USACO,只打了 Bronze 就跑路了.不得不说也有很有意思的题目.接下来就看看题目吧. 由于现在还看不到题目,只给出加工后的题目大意. T1 Lonely Photo Conten ... 
- csp-s 2021
		T1 廊桥分配 当一架飞机抵达机场时,可以停靠在航站楼旁的廊桥,也可以停靠在位于机场边缘的远机位. 乘客一般更期待停靠在廊桥,因为这样省去了坐摆渡车前往航站楼的周折. 然而,因为廊桥的数量有限,所以这 ... 
- CSP-J/S 2021 游记
		\(\large\texttt{Day -1}\) 晚上好累啊,去集训了,回来之后发现十一点了还码了一会儿,只能祈求上帝明天不会打瞌睡. \(\large\texttt{Day 0}\) 意料中的事情 ... 
随机推荐
- 关于AS下Gradle安装问题总结
			在之前安装AS的随笔中简单描述了解决方法,但不够详细,在第二次创建项目时又遇到了gradle安装错误,通过在网上查找解决方法,发现方法比较多样,且描述不够仔细,本随笔将详细记录我在gradle安装中的 ... 
- css3 横屏
			@media screen and (orientation: portrait) { html{ width : 100vmin; height : 100vmax; } body{ width : ... 
- hibernate 初学
			1. hibernate的基本操作 执行流程: 执行流程细节:基本的配置文件 可以与mybatis进行对比着记 hibernate 的主键生成策略 ... 
- 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 百篇博客分析OpenHarmony源码 | v53.02
			百篇博客系列篇.本篇为: v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ... 
- Phalcon如何切换数据库《Phalcon入坑指南系列 三》
			本系列目录 一.Phalcon在Windows上安装 <Phalcon入坑指南系列 一> 二.Phalcon入坑必须知道的功能(项目配置.控制器.模型.增.删.改.查) 三.Phalcon ... 
- 转载 使用wce进行本地和域的hash注入
			参数解释:-l 列出登录的会话和NTLM凭据(默认值)-s 修改当前登录会话的NTLM凭据 参数:<用户名>:<域名>:<LM哈希>:<NT哈希>-r ... 
- 常见的==和equals比较
			在笔试上碰到很多这样类似的题,全部整理到这里 String a = "Hello"; String b = "Hello"; String c = new St ... 
- ☠全套Java教程_Java基础入门教程,零基础小白自学Java必备教程👾#010 #第十单元 Scanner类、Random类 #
			一.本单元知识点概述 (Ⅰ)知识点概述 二.本单元教学目标 (Ⅰ)重点知识目标 1.API的使用2.Scanner类的使用步骤3.Random类的使用 (Ⅱ)能力目标 1.掌握API的使用步骤2.使用 ... 
- DL4J实战之一:准备
			欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ... 
- 10.6.2 sendfile
			1.传统Linux中 I/O 的问题 2.传统的 Linux 系统的标准 I/O 接口( read. write)是基于数据拷贝的,也就是数据都是 copy_to_user 或者 copy_from_ ... 
