专栏文章
题解:P14222 [ICPC 2024 Kunming I] 收集硬币
P14222题解参与者 3已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @minlxjet
- 此快照首次捕获于
- 2025/12/02 04:34 3 个月前
- 此快照最后确认于
- 2025/12/02 04:34 3 个月前
P14222 [ICPC 2024 Kunming I] 收集硬币 题解
题目大意
有 个格子排成一行,两个机器人以相同的最大速度 移动。有 枚硬币在第 秒出现在格子 ,如果此时有机器人在该格子就能收集硬币。求能收集所有硬币的最小速度 ,如果不可能输出 。
思路
我们发现速度 具有单调性:如果某个速度可行,那么更大的速度一定也可行。因此可以用二分查找来求最小速度。
对于每个二分的速度 ,我们需要验证是否存在两个机器人的移动方案能够收集所有硬币。
验证算法
我们维护两个机器人的可能位置范围:
- 设参考点 表示最近一次确定位置的硬币
- 区间 表示另一个机器人当前可能的位置范围
对于每个新的硬币 :
- 计算时间差 ,即机器人最大移动距离
- 判断两个机器人能否到达当前硬币位置:
- 参考点机器人能到达:
- 另一个机器人能到达:
根据判断结果更新状态:
- 两个都能到达:更新区间为交集, 移到当前点
- 只有参考点能到达:扩展区间, 移到当前点
- 只有另一个能到达:重置区间, 移到当前点
- 都不能到达:当前速度不可行
代码实现
CPP#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int n;
struct node {
int v, t; // v: 位置坐标, t: 时间
} a[N];
// 按时间排序
bool cmp(node x, node y) {
return x.t < y.t;
}
// 检查给定速度mid是否可行
bool check(int mid) {
int p = 1; // 参考点索引,表示最近确定位置的硬币
int l = 1, r = 1e9; // 另一个机器人的可能位置区间[l, r]
for (int i = 2; i <= n; i++) {
// 计算时间差对应的最大移动距离
int d = (a[i].t - a[p].t) * mid;
// 判断两个机器人是否能到达当前硬币位置
bool x1 = (a[p].v + d >= a[i].v) & (a[p].v - d <= a[i].v); // 参考点机器人能到达
bool x2 = (r + d >= a[i].v) & (l - d <= a[i].v); // 另一个机器人能到达
// 根据两个机器人的可达性进行状态转移
if (x1 && x2) {
// 两个机器人都能到达:取交集更新区间
l = min(a[p].v - d, l - d);
r = max(a[p].v + d, r + d);
p = i; // 更新参考点为当前点
} else if (x1) {
// 只有参考点机器人能到达:扩展另一个机器人的区间
p = i;
l = l - d; // 区间左边界扩展
r = r + d; // 区间右边界扩展
} else if (x2) {
// 只有另一个机器人能到达:重置区间
l = a[p].v - d; // 以参考点为中心重置左边界
r = a[p].v + d; // 以参考点为中心重置右边界
p = i; // 更新参考点为当前点
} else {
// 两个机器人都无法到达当前硬币
return 0; // 当前速度不可行
}
// 确保区间在有效范围内[1, 10^9]
l = max(l, 1ll);
r = min(r, 1000000000ll);
}
return 1; // 所有硬币都能被收集
}
void solve() {
int ans = -1;
cin >> n;
// 读入硬币数据
for (int i = 1; i <= n; i++) {
cin >> a[i].t >> a[i].v;
}
// 按时间排序硬币
sort(a + 1, a + n + 1, cmp);
// 二分查找最小速度
int l = 0, r = 1e9, mid;
while (l < r) {
mid = l + r >> 1; // 取中间速度
if (check(mid)) {
ans = mid; // 当前速度可行,尝试更小的速度
r = mid;
} else {
l = mid + 1; // 当前速度不可行,需要更大的速度
}
}
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
这道题的时间复杂度是:
- 排序:
- 二分查找:
- check 验证:
3 秒的时限应该是可以过的
注意事项
- 排序:硬币必须按时间排序,因为机器人移动依赖于时间顺序
- 区间维护:使用 区间表示另一个机器人的可能位置,避免精确跟踪具体位置
- 边界处理:每次更新区间后要限制在 范围内
- 状态转移:根据两个机器人的可达性采用不同的更新策略,确保至少有一个机器人能到达每个硬币位置
感谢阅读。
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...