Kick Start 2019 Round A Contention
题目大意
一排
有
可以按任意顺序处理这
试问每个请求最终订到的座位数量的最小值最大可能是多少?
Limits
- Time limit: 30 seconds per test set.
- Memory limit: 1GB.
- Number of test cases
. . . .- For at least 85 of the test cases,
.
分析
Observation 1: 第
Observation 2:
设
注意:满足上述条件的处理顺序也许不唯一,因为
到这里我就想不通了。
官方题解
We can observe that for a chosen ordering of the requests, the number of seats that the system books in the last request does not depend on the ordering of the previous
requests. So, we could start by finding the request to be processed last and move backwards towards the earlier requests.
这一段我能理解,但问题在于 how can you determine which request should be processed last in an optimal solution?
我想通了。
考虑若干个请求的任意排列,按此排列进行操作,把每个请求获得的座位数的最小值简称为此「排列的最小值」。
Key observation:
从任意
( )个请求的任意排列中拿走一个请求,余下的 请求的排列的最小值一定不小于原先 个请求的排列的最小值。
定义函数
对于
假设
考虑排列
对比
考虑排列
由于
官方题解的做法是根据上述贪心策略,用数据结构辅助求解
接下来给出另一种做法:二分答案。
Another approach:二分答案
这种做法来自 Mahmoudian 的 submission,其思路比官方解法更为简单,巧妙而富有启发性。
此做法的最坏时间复杂度是
我对其代码做了少许修改并加了注释
const int Q = 30000;
int n, q, xl[Q], xr[Q], mvl[Q]; // mvl意思是move l to a new index
pair<int, int> sr[Q]; // sr 代表 sort
int check(int k) {
for (int j = 0; j < q; ++j)
mvl[j] = xl[j];
for (int j = 0; j < q; ++j) {
int r = xr[j];
int st = mvl[j]; // st即start,表示当前能分配给j的那一段的左端点
int allowed_after = r;
int cnt = 0;
// 判断能否在[l,r]中分配k个座位给j
for (int i = j + 1; i < q; ++i) {
// 遍历被j包含的那些区间
if (xl[i] >= r) break;
if (xr[i] <= r) {
// 被j包含的区间一定要在j之前处理,不然这些区间一个座位也得不到
// 换言之,j得不到它所包含的那些区间内的座位
if (xl[i] <= st) {
st = max(st, xr[i]);
} else {
cnt += xl[i] - st;
st = max(st, xr[i]);
if (cnt >= k) {
// j 已经获得k个座位。
// allowed_after 之后的那些座位,留给别的区间。
allowed_after = xl[i] - (cnt - k);
break;
}
}
}
}
if (cnt < k) {
cnt += r - st;
if (cnt < k) return 0;
allowed_after = r - (cnt - k);
}
//排序在j之后,与j有交集但没被j包含的那些区间,
//如果左端点在allowed_after之前,那么这个区间要放在j之后处理,
//否则j获得座位就达不到k个。
for (int i = j + 1; i < q; ++i) {
if (xl[i] >= allowed_after) break;
if (xr[i] > r) {
//这一句的效果就相当于把i放在j之后处理
mvl[i] = max(mvl[i], r);
}
}
}
return 1;
}
int main() {
int tc;
cin >> tc;
for (int tt = 1; tt <= tc; ++tt) {
cin >> n >> q;
for (int j = 0; j < q; ++j) {
// 区间采用左闭右开表示,座位改成从0开始编号
cin >> xl[j] >> xr[j], --xl[j];
sr[j] = {xl[j], -xr[j]};
}
//区间按左端点从小到大排序;左端点相同的,按右端点从大到小排序;
//效果是若区间A包含区间B,则B排在A后面
sort(sr, sr + q);
for (int j = 0; j < q; ++j)
xl[j] = sr[j].first, xr[j] = -sr[j].second;
int bl = 0, br = n;
for (int j = 0; j < q; ++j)
br = min(br, xr[j] - xl[j]);
++br;
while (bl < br - 1) {
int bm = (bl + br) / 2;
if (check(bm)) {
bl = bm;
} else {
br = bm;
}
}
cout << "Case #" << tt << ": " << bl << '\n';
}
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 2025成都.NET开发者Connect圆满结束
2015-10-03 hihocoder 1169 猜数字