二分答案 跳石头 笔记
STL的函数 lower_bound 与 upper_bound
意义:不用手写二分查找
lower_bound 找出序列中第一个大于等于x的数
upper_bound 找出序列中第一个大于x的数
写法 :lower_bound(a+1,a+n+1,x)
或 lower_bound(a+1,a+n+1,x,cmp)
前提 a为升序序列
若为降序
则写做 lower_bound(a+1,a+n+1,x,greater<int>())
重点例题:跳石头
题目背景
一年一度的“跳石头”比赛又要开始了!
题目描述
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 NNN 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 MMM 块岩石(不能移走起点和终点的岩石)。
输入格式
第一行包含三个整数 L,N,ML,N,ML,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1L \geq 1L≥1 且 N≥M≥0N \geq M \geq 0N≥M≥0。
接下来 NNN 行,每行一个整数,第 iii 行的整数 Di(0<Di<L)D_i( 0 < D_i < L)Di(0<Di<L), 表示第 iii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出格式
一个整数,即最短跳跃距离的最大值。
输入输出样例
25 5 2 2 11 14 17 21
4
说明/提示
输入输出样例 1 说明:将与起点距离为 222和 141414 的两个岩石移走后,最短的跳跃距离为 444(从与起点距离 171717 的岩石跳到距离 212121 的岩石,或者从距离 212121 的岩石跳到终点)。
另:对于 20%20\%20%的数据,0≤M≤N≤100 ≤ M ≤ N ≤ 100≤M≤N≤10。
对于50%50\%50%的数据,0≤M≤N≤1000 ≤ M ≤ N ≤ 1000≤M≤N≤100。
对于 100%100\%100%的数据,0≤M≤N≤50,000,1≤L≤1,000,000,0000 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,0000≤M≤N≤50,000,1≤L≤1,000,000,000。
1.对于一个可能的"最大的"最短跳跃距离 可能只是一个可行解, 而不是最优解;
在一个区间上可能有很多个可行解, 这些解都满足要求, 但不一定是最优的, 我们要考虑所有的可行解, 并从中找到一个最优解作为我们的答案.
2.对于这一题, 若x为可行解, 那么[1, x) 上的数一定可行, 但一定不是最优, 故最优解一定在[x, L] (注:L为总长度)这个区间上. 如果x不可行, 那么[x, L]上的解一定都不可行 (√ )
非常重要
3.用二分最短的跳跃距离,把这个距离"认为"是最短的跳跃距离, 以这个标准去去掉石头,
先不必考虑移走石头数的限制, 待全部拿完后, 再与最大移动数目M进行比较,
若超过了, 那么就说明在最多移走M块石头的限制条件下不能达到这个最短跳跃距离, 这就是一个不可行解, 反之就是一个可行解。
4.模拟
模拟这个跳石头的过程。
开始你在 i (i = 0) 位置,我在跳下一步的时候去判断我这个当前跳跃的距离,如果这个跳跃距离比二分出来的mid小,那这就是一个不合法的石头,应该移走。
理由 :我们二分的是最短跳跃距离,已经是最短了,如果跳跃距离比最短更短则显然不合法;
移走后,计数器++,再考虑继续向前跳。
去看移走之后的下一块石头,再次判断跳过去的距离,如果这次的跳跃距离比最短的长,则这样跳合法,我们就跳过去,继续判断,
如果跳过去的距离不合法就再拿走;
这样不断进行这个操作,直到i = n+1
理由 :河中间有n块石头,则终点在n+1处。(这里千万要注意不要把n认为是终点,实际上从n还要跳一步才能到终点)。
模拟完这个过程,我们查看计数器的值,这个值代表的含义是我们以mid作为答案需要移走的石头数量;
然后判断这个数量 是不是超了就行。如果超了就返回false,不超就返回true;
CODE:
核心函数 judge
1 bool judge(int x){ 2 int tot = 0;//需要移动的总数 3 int i = 0; 4 int now = 0;//当前所在的石头 5 6 while(i < n + 1){ 7 i++; 8 //如果当前石头与下一块石头之间的距离比我们设定的最短距离要小 则移走当前石头 9 //tot++代表需要移动的加一,i++代表接下来判断移动的石头后面的一块与当前'now'这块石头的距离 10 if (a[i] - a[now] < x){ 11 //那么就将这块石头拿走 12 tot++; 13 } 14 else { 15 //如果满足的话 16 now = i; 17 } 18 } 19 if(tot > m) { 20 return false; 21 } 22 else{ 23 //否则 24 return true; 25 } 26 }
完整AC代码:
1 #include<bits/stdc++.h> 2 #define maxn 500010 3 using namespace std; 4 5 int l,r,n,m,ans,mid,d; 6 int a[maxn]; 7 8 bool judge(int x){ 9 int tot = 0;//需要移动的总数 10 int i = 0; 11 int now = 0;//当前所在的石头 12 13 while(i < n + 1){ 14 i++; 15 //如果当前石头与下一块石头之间的距离比我们设定的最短距离要小 则移走当前石头 16 //tot++代表需要移动的加一,i++代表接下来判断移动的石头后面的一块与当前'now 这块石头的距离 17 if (a[i] - a[now] < x){ 18 //那么就将这块石头拿走 19 tot++; 20 } 21 else { 22 //如果满足的话 23 now = i; 24 } 25 } 26 if(tot > m) { 27 return false; 28 } 29 else{ 30 //否则 31 return true; 32 } 33 } 34 35 int main(){ 36 cin>>d>>n>>m; 37 38 for(int i = 1;i<=n;i++){ 39 cin>>a[i]; 40 } 41 42 a[n + 1] = d; 43 44 l = 1,r = d; 45 while (l <= r){ 46 //二分枚举 47 mid = (l + r) / 2; 48 if (judge(mid)) { 49 //mid是可行解的情况 50 ans = mid; 51 l = mid + 1; 52 } 53 //mid不是可行解的情况 54 else r = mid - 1; 55 } 56 cout<< ans <<endl; 57 return 0; 58 }
完结;
参考博客:
————————————————
https://blog.csdn.net/weixin_44179892/article/details/10419701
感谢!!

浙公网安备 33010602011771号