复赛四:跳石头
洛谷:P2678
OJ: I4953
问题分析:
- 给定起点和终点,以及它们之间的 N 块岩石,选手必须从起点跳到终点,每次只能跳到相邻的岩石。
- 组委会想要移除最多 M 块岩石,使得选手在比赛过程中最短的跳跃距离尽可能长。
- 我们需要找到移除岩石的方案,使得最短的跳跃距离最大化。
解决方案:
- 二分查找:
- 我们可以将最短跳跃距离视为一个搜索空间的变量 D,范围从 0 到 L。
- 通过二分查找的方法,我们尝试不同的 D,检查在给定的 D 下,是否可以通过移除不超过 M 个岩石,使得所有相邻岩石之间的距离都大于等于 D。
- 如果在某个 D 下可行,我们尝试更大的 D;如果不可行,我们尝试更小的 D。
- 可行性检查函数
isPossible:- 遍历所有的岩石位置,计算每一跳的距离。
- 如果当前岩石与上一个未被移除的岩石之间的距离小于 D,则需要移除当前岩石,计数器
cnt加一。 - 如果
cnt超过了M,则在当前的 D 下不可行,返回false。 - 如果遍历完成且
cnt不超过M,说明在当前的 D 下可行,返回true。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /**************************************************************** * Description: 2015_S_semi_4 跳石头 * Author: Alex Li * Date: 2024-09-19 19:46:20 * LastEditTime: 2024-09-19 20:25:01 ****************************************************************/ #include <iostream> #include <vector> #include <algorithm> using namespace std; // 检查在最小跳跃距离为 D 的情况下,是否可以通过移除不超过 M 个岩石,从起点跳到终点 bool isPossible(const vector<int>& positions, int N, int M, int D) { int cnt = 0; // 已移除的岩石数量 int prev = 0; // 上一个未被移除的岩石在 positions 中的索引,初始为起点 int size = positions.size(); // 岩石总数(包括起点和终点) // 遍历所有岩石,从起点开始 for (int i = 1; i < size; ++i) { // 计算当前岩石与上一个未被移除的岩石之间的距离 int distance = positions[i] - positions[prev]; if (distance < D) { // 如果距离小于最小跳跃距离 D,需要移除当前岩石 cnt++; if (cnt > M) { // 如果移除的岩石数量超过允许的最大值 M,则无法实现,返回 false return false; } // 当前岩石被移除,prev 不变,继续检查下一块岩石 } else { // 如果距离大于等于 D,更新 prev 为当前岩石的索引 prev = i; } } // 遍历完成且移除的岩石数量不超过 M,返回 true return true; } int main() { int L, N, M; cin >> L >> N >> M; // 读取起点到终点的距离 L,岩石数量 N,最多可移除的岩石数 M vector<int> positions(N + 2); // 创建一个大小为 N+2 的向量,包含起点和终点 positions[0] = 0; // 起点位置为 0 for (int i = 1; i <= N; ++i) { cin >> positions[i]; // 读取第 i 块岩石的位置 } positions[N + 1] = L; // 终点位置为 L sort(positions.begin(), positions.end()); // 对所有岩石位置进行排序 int low = 0; // 二分查找的下界,最小可能的最小跳跃距离 int high = L; // 二分查找的上界,最大可能的最小跳跃距离 // 开始二分查找,寻找最大化的最小跳跃距离 while (low < high) { // 取中间值,向上取整,避免无限循环 int mid = (low + high + 1) / 2; // 检查在最小跳跃距离为 mid 的情况下,是否可行 if (isPossible(positions, N, M, mid)) { // 如果可行,说明可以尝试更大的最小跳跃距离,更新 low low = mid; } else { // 如果不可行,说明最小跳跃距离过大,更新 high high = mid - 1; } } // 输出最大化的最小跳跃距离 cout << low << endl; return 0; } |
