P2678 [NOIP2015 提高组] 跳石头

大致题意

从距离为\(L\)的起点到终点,其中有\(N个\)岩石,然后想要使最短跳跃距离最大化,最多可以移走\(M\)块岩石

解题思路

最初看到题时,会从题目出发,计算岩石的最短距离,然后进行更新

但是直接模拟的方法太笨了

可以转换一下思路,假设我们在寻找到当前石头距离不小于\(k的石头,如果遇到距离小于k的石头,就把它移走\),那么这样就比较好理解了

\(我们只需要维护一个k,即为最短跳跃距离,只要使这个k尽可能大且移走石块数量不超出M,即为答案\)

具体实现

题目中有一个关键词,是整数二分出现时题目中经常会出现的用词:最短跳跃距离的最大值

整数二分经常在有类似'最大化最小值',或者是'最短距离的最大值'这样字眼的题目中出现。

这里用到了整数二分的计算方法,左边界\(l\)的值可以设成0,右边界\(r\)的值可以设为起点到终点的总距离,然后进行二分

\(检查过程则是设定一个距离x,从起点开始跳跃。如果第i个石头和当前石头距离小于x,则移走第i个石头,否则跳跃到第i个石头上\)

\(最后再用总共移走的石头数量和M比较,看当前结果是否合法\)

为了格式的严整,这里把检查过程写入一个函数check(int x)里

二分代码模板:

// l, r, ans已定义且l, r已被合理赋值

while(l <= r){
	int mid = l + r >> 1;
	if(check(mid)){
		ans = mid;
		l = mid + 1;
	} else r = mid - 1;
}

// output ans

代码实现

// P2678 (NOIP2015 提高组) - 跳石头
// 6 Mar 2021 by zqsml
#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 50010;

int len, n, m;
int rock[N];

bool check(int num){
	int ind, cur;
	ind = 0, cur = 0;
	int count = 0;
	while(ind < n + 1){
		ind ++ ;
		if(rock[ind] - rock[cur] < num) count ++ ;
		else cur = ind;
	}
	if(count > m) return false;
	return true;
}

int main(){
	scanf("%d %d %d", &len, &n, &m);
	for(int i = 1; i < n + 1; i ++ ) scanf("%d", &rock[i]);
	rock[n + 1] = len;

	int ans = 0;
	int l = 0, r = len;
	while(l <= r){
		int mid = l + r >> 1;
		if(check(mid)){
			ans = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2021-03-07 13:46  kmiao03  阅读(296)  评论(0)    收藏  举报