一:核心步骤:
1.确定答案的范围:找到最小可能值(left)和最大可能值(right);
2.二分查找:在left和right里面进行二分查找,每次取中间值mid;
3.bool check函数:用于判断mid是否可行;
(1):如果check返回true,说明这个mid可能是答案,调整搜素范围找更优解
(2):如果check返回false,说明mid不对,调整搜索范围
4.调整搜索范围:
根据(3)中check函数返回的结果,如果返回true,则更新right为mid(寻找更小的答案),反之,如果返回false,则更新left为(mid+1)(更大的答案);
例题:P2678 [NOIP 2015 提高组] 跳石头 - 洛谷
二:P2678复盘:
条件梳理:
l:从起点到终点的距离;n:n块石头;m:最多移走m块石头;stones:存放每个石头距离的数组
1:check函数参数x:在移走不超过m个石头下,假设最短跳跃距离为x;
ps:check函数的判断逻辑:
(1):遍历每一块石头,计算这一块石头与前一块的距离,如果是第一块,就是与起点的距离;
(2):定义计数器count,用于记忆一共移走了多少块石头;
(3):如果距离小于判断参数x,则移走,count++;如果大于等于,则不做操作;
(4):如果移走的数量count大于m,则返回false,反之返回true;
2:定义l,n,m三个变量以及存储每一块石头距离的数组为全局变量,以便check函数操作;
3:在main函数里面对答案二分,其中左指针(左边界)为0(起点),右指针(右边界)为l(全长);
ps:在不越界的范围内:如果返回true,则缩小范围继续找更优解,如果返回false,则改变范围找符合的值;
附代码:
#include <bits/stdc++.h>
using namespace std;
vector<int> stones;
int l = 0, n = 0, m = 0;
// l:起点到终点的距离,n:石头数量;m:最多移走的石头数量
bool check(int x)
{
int last = 0;// 初始化last为起点,for循环中从第一块石头减去起点开始
int num = 0;// 初始化移走石头的数量
for (int i = 0; i < n; i++) // 遍历每一块石头
{
if (stones[i] - last < x)
{
num++;
}
else
{
last = stones[i];
}
if (num > m)
return false;
}
// 检查终点与最后一个岩石;
if (l - last < x)
num++;
return num <= m;
}
int main()
{
cin >> l >> n >> m;
stones.resize(n);
for (int i = 0; i < n; i++)
cin >> stones[i];
// 输入完毕,开始计算
sort(stones.begin(), stones.end()); // 确定石头是按距离排序
int left = 0, right = l; // 初始化二分答案的左右边界
// 左边界为起点,右边界为终点到起点的距离l;
// 左右指针说明了答案可能的范围,最小为0,最大为l
int ans = 0; // 初始化答案
while (left <= right)
{
int mid = left + (right - left) / 2; // 计算中间值
if (check(mid))
{
ans = mid;
left = mid + 1;//比mid小的值一定符合,所以在更大值的范围里面找
}
else
{
right = mid - 1;//比mid更大的值一定不符合,所以在更小值的范围里找
}
}
cout << ans << endl;
return 0;
}