Bootstrap

洛谷P2678 [NOIP2015 提高组] 跳石头

传送门:https://www.luogu.com.cn/problem/P2678

非常同意一个观点:二分答案由二分区间和judge函数构成

二分答案,顾名思义,就是找到答案的范围区间,然后在这个区间里面去二分查找最优答案

该题目的答案区间显而易见就是0到L,那么接下来就是judge函数的处理了

可以这样考虑,对于一个答案,我假设它是正确的的,那么要达到满足这个答案的状态我可能需要去移动石头,先不考虑题目中移走石头个数的限制,只要两个石头之间不满足答案,我就移走一块,那在满足答案的时候,我可能移走了超过题目限制的石头。这说明为了这个答案是偏大的,因为我需要移走更多的石头才能达到答案的差值,所以我需要缩短有区间来将答案调小,反之,如果我要移走的石头小于等于题目限制,说明这个答案是可行的,但由于要求的是最大值,我们的答案虽然符合,但也许不是最大,那我就把这个答案及记录下来并缩短左区间查找看是否有更大的答案

上代码

#include<iostream>
#define MAX 50050
using namespace std;
int Array[MAX], L, N, M;
bool Judge(int length)
{
	int now = 0, count = 0;
	//now记录当前在第几块石头
	//count表示在当前答案下需要移走几块石头才能满足
	for (int i = 1; i <= N + 1; i++)
	{
		if (Array[i] - Array[now] < length)count++;
		else now = i;
		//如果前面一块石头跟现在的位置差小于最大值
		//就需要移走这块石头,否则前进,跳到下一块石头
	}
	if (count > M)return false;
	else return true;
	//如果需要移走的石头大于题目给定值
	//说明这个答案一定不能满足条件且偏大,那么
	//就需要缩短右边界来让答案变小一点
	//否则答案满足,但是可能有最优解,缩短左边界
}
int main(void)
{
	cin >> L >> N >> M;
	for (int i = 1; i <= N; i++)
		cin >> Array[i];
	Array[N + 1] = L;
	int left = 0, right = L + 1, mid = 0, ans = 0;
	//答案区间在0到L+1直接
	while (left <= right) {
		mid = (left + right) / 2;
		if (Judge(mid)) { left = mid + 1; ans = mid; }
		//用一个ans变量记录答案是一种好方法,不易错
		else right = mid - 1;
	}
	cout << ans;
	return 0;//完结撒花
}

;