藍橋杯-跳石頭(二分法)

DawnTraveler發表於2024-04-12

0.題目

題目描述

一年一度的「跳石頭」比賽又要開始了!

這項比賽將在一條筆直的河道中進行,河道中分佈著一些巨大岩石。組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 N 塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。

為了提高比賽難度,組委會計劃移走一些岩石,使得選手們在比賽過程中的最短跳躍距離儘可能長。由於預算限制,組委會至多從起點和終點之間移走M 塊岩石(不能移走起點和終點的岩石)。

輸入描述

輸入檔案第一行包含三個整數 L,N,M,分別表示起點到終點的距離,起點和終點之間的岩石數,以及組委會至多移走的岩石數。

接下來 N 行,每行一個整數,第 i 行的整數 Di(0 < Di < L)表示第 i 塊岩石與起點的距離。這些岩石按與起點距離從小到大的順序給出,且不會有兩個岩石出現在同一個位置。

其中,0≤M≤N≤5×104 ,1≤L≤109 。

輸出描述

輸出只包含一個整數,即最短跳躍距離的最大值。

樣例輸入
25 5 2
2
11
14
17
21

樣例輸出
4

1.題解

1.0 暴力列舉(超時)

思路

列舉\(C_n^m\)種情況,分別求得個情況的最小d,挑出其中的最大值

1.1 二分法

思路

同樣都是列舉,這裡我不列舉你的情況了,我列舉所有可能的d值,可能的d值正好排成一個有序單調遞增序列(適合二分查詢)
這裡直接套上二分模板, 但是注意這裡的判斷條件由之前的 a[mid] >= n / <= n
改變為根據我們這題實際需求滿足能透過移動m塊石頭使每個間距均大於等於d的情況(check函式)
這裡可以透過貪心思想轉換為移動不多於m塊石頭使每個間距均大於等於d的情況, 因為我如果使用移動少於m塊石頭都能滿足條件,移動更多石頭只會使間距更大,依舊滿足條件!

程式碼

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e5;
int stone[Maxn];
int len, n, m;

// 檢測能否透過移動 m塊石頭達成最小距離>d 
bool check(int d) {
	int pos = 0; // 當前位置 
	int num = 0; // 總共移走的石頭數目 
	for(int i = 1; i <= n+1; i++){
		// 這裡可以這樣理解, (由於起點不可動)前面都是如果不滿足,就移走stone[i]這塊石頭
		// 否則保留這開始石頭,更新我目前的位置到stone[i]
		// 但是最後如果stone[n+1] - pos < d?怎麼辦,終點石頭不能移,好辦,移走其前面一塊(pos)石頭即可,特殊思考一下,但不需要特殊處理 
		if(stone[i] - pos < d){
			num++;
			if(num > m) return false;
		} else{
			pos = stone[i];
		}
	}
	return true;
}
int main() {
	cin >> len >> n >> m;
	stone[n+1] = len; 
	for(int i = 1; i <= n; i++) {
		cin >> stone[i];
	}
	int l = 0, r = len;
	while(l < r) {
		int d = (r - l + 1) / 2 + l;
		if(check(d)) {
			l = d;
		} else {
			r = d - 1;
		}
	}
	cout << l;
	return 0;
}