P8775 [蓝桥杯 2022 省 A] 青蛙过河 题解

题目传送门

题面

小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。

河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降 1,当石头的高度下降到 0 时小青蛙不能再跳到这块石头上(某次跳跃后使石头高度下降到 0 是允许的)。
小青蛙一共需要去学校上
x 天课,所以它需要往返 2x 次。当小青蛙具有一个跳跃能力 y 时,它能跳不超过 y 的距离。

请问小青蛙的跳跃能力至少是多少才能用这些石头上完x 次课。

前置题目

可以先去看一看这道题

看完这道题之后,不难发现(显而易见的),两道题几乎一模一样 (就是不知道为什么青蛙过河明明是进阶版的确是绿题) ,青蛙过河只不过是在这道题的基础上加了一个二分

分析

前置题目的思路我也讲一下

算了还是引用大佬的题解罢

  
先说结论:

最多通过的青蛙的数量就是每个长度为 l 的区间中石头数量的最小值。

我们考虑一段长度为 l 的区间,

因为青蛙最多跳 l 的距离,

不难想到,每只青蛙都一定会在这个区间落下至少一次。

因为一定可以从另一个石子数大于等于 n 的区间跳过来,

所以这样取出的答案是可行的。

那么我们就得到了结论:

最多通过的青蛙的数量就是每个长度为 l 的区间中石头数量的最小值。

青蛙过河分析

有了前置题目,我们就可以把题目转化为求一个最小的数y使得有2x只青蛙能跳过河(差不多就是前置题目倒过来)

那这样就可以轻易的想到用二分的方法

关于可二分性

其实这个不难说明,假设 y1 < y2,倘若青蛙在y1时能跳过河,那么青蛙在y2的时候就一定可以跳过去(可以理解成y2包含y1)

二分范围

很显然我写这个是为了水字数

青蛙想跳过河,那么青蛙的最小跳跃范围至少要是1,最大的自然就是一步跳到和对岸也就是n

代码实现

check函数

既然已经有了前置题目,那我们只需要把前置题目的代码复制过来缝合一下就好了

bool check(int l){
	int cnt = INF;//赋为一个很大的数 跟大佬的rp一样高
	for(int i = l+1; i <= n; i++){
		cnt= min(cnt,b[i]-b[i-l]);
	}
	return cnt >= 2*x;
}

然后你满心欢喜的去提交代码

#include 
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e7+5;
int n, x;
int a[N],b[N];
int ans = INF;
bool check(int l){
	int cnt = INF;
	for(int i = l+1; i <= n; i++){
		cnt= min(cnt,b[i]-b[i-l]);
	}
	return cnt >= 2*x;
}
int main(){
	cin >> n >> x;
	for(int i = 2; i <= n; i++){
		cin >> a[i];
		b[i] = b[i-1] + a[i];
	}
	int l = 1, r = n;
	while(l < r){
		int mid = (l + r) >> 1;
		if(check(mid)){
			r = mid; 
			ans = mid;
		}else{
			l = mid+1;
		}
	} 
	cout << ans << endl;
	return 0;
}

但是却WA了一个点

打油诗一首

我是一只小青蛙,我在洛谷养青蛙。

我让青蛙去上学,青蛙回我WA WA WA。

因为我们在二分的时候没有判断一步跳到的情况的

但是很显然一步到位的情况是绝对存在的

所以我们只需要把ans赋值为n

丑陋的AC代码

#include 
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e7+5;
int n, x;
int a[N],b[N];
int ans = INF;
bool check(int l){
	int cnt = INF;
	for(int i = l+1; i <= n; i++){
		cnt= min(cnt,b[i]-b[i-l]);
	}
	return cnt >= 2*x;
}
int main(){
	cin >> n >> x;
	ans = n;
	for(int i = 2; i <= n; i++){
		cin >> a[i];
		b[i] = b[i-1] + a[i];
	}
	int l = 1, r = n;
	while(l < r){
		int mid = (l + r) >> 1;
		if(check(mid)){
			r = mid; 
			ans = mid;
		}else{
			l = mid+1;
		}
	} 
		cout << ans << endl;
	return 0;
}

你可能感兴趣的:(#,NOIP,信奥赛题目解析,蓝桥杯,算法)