二分答案 跳石头 笔记

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 1L1 且 N≥M≥0N \geq M \geq 0NM0。

接下来 NNN 行,每行一个整数,第 iii 行的整数 Di(0<Di<L)D_i( 0 < D_i < L)Di(0<Di<L), 表示第 iii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

一个整数,即最短跳跃距离的最大值。

输入输出样例

输入 #1
25 5 2 
2
11
14
17 
21
输出 #1
4

说明/提示

输入输出样例 1 说明:将与起点距离为 222和 141414 的两个岩石移走后,最短的跳跃距离为 444(从与起点距离 171717 的岩石跳到距离 212121 的岩石,或者从距离 212121 的岩石跳到终点)。

另:对于 20%20\%20%的数据,0≤M≤N≤100 ≤ M ≤ N ≤ 100MN10。

对于50%50\%50%的数据,0≤M≤N≤1000 ≤ M ≤ N ≤ 1000MN100。

对于 100%100\%100%的数据,0≤M≤N≤50,000,1≤L≤1,000,000,0000 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,0000MN50,000,1L1,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

 

感谢!!

 

posted @ 2022-02-10 10:02  November&&Rain  阅读(68)  评论(0)    收藏  举报