思路:
形如求最小值的最大值以及求最大值的最小值都可以二分求解。
就像这道题,求最短的跳跃距离尽可能长(就是求最小值的最大值),注意到跳跃距离肯定在1至L之间,于是问题就变为在1~L之间求可行解的最大值,可行解就是跳跃距离的最小值,如果照着题目的思路思考,我们的思维可能会被只能移走M快岩石限制住,因此我们可以逆向思考,从结果入手:每次取1-L中mid的值,把每次的mid当作跳跃距离,然后判断跳跃距离为mid时需要移动的岩块sum为多少。如果sum<=M,那就说明这个最大值mid可以作为最大值,又因为1-L单调增,因此可能mid右边的也可以作为跳跃距离,所以往右边接着找;如果sum>M,那就说明这个最大值mid不能作为最大值,因为跳跃距离太大了,需要移动的石块sum也很多,因此我们得把跳跃距离弄小点,往mid左边寻找。
其中check()函数就是判断跳跃距离为mid时,需要跳过多少个石块
代码:
package binaryFindAndAnswer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class P2678 {
static int L, N, M;
static int[] arr;//存储每个石块的距离
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
L = (int) st.nval;
st.nextToken();
N = (int) st.nval;
st.nextToken();
M = (int) st.nval;
arr = new int[N + 2];// arr初始化,实际上N+2块石头(包括头尾两块)
for (int i = 1; i <= N; i++) {
st.nextToken();
arr[i] = (int) st.nval;
}
arr[N + 1] = L;// 注意L才是终点
int l = 0, r = L;
// 开始对
// int ans = 0;//也可以用ans记录mid
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {//如果mid可以作为最大值,又因为l~r单调增,说明mid右边也有可能,因此l = mid + 1
l = mid + 1;
// ans = mid;
}else {//如果不可以作为最大值,说明最大值在左边
r = mid - 1;
}
}
//输出当check(mid)为ture时的mid,此时mid=l-1
System.out.println(l-1);
//也可以用ans记录每次mid可行时的mid,并输出
// System.out.println(ans);
}
public static boolean check(int mid) {
int sum = 0,now = 0;//now代表现在所在的石块
for (int i = 1; i < N+2; i++) {
//因为此时我们假设此时的跳跃距离mid就是最大值,如果两个石头间隔小于mid那就说明可以拿走
//如果两个石头距离大于mid,我们就根本跳不过去,所以这块石头不能拿掉,只好在下块石头接着跳
if (arr[i] - arr[now] < mid) {//说明可以拿走
sum++;
}else {//说明不能拿走
now = i;//那我们就到这一块石头上去继续判断
}
}
if (sum > M) {//当要移的石块大于M
return false;
}else {//当要移动的石块小于等于M,说明此mid可行
return true;
}
}
}