2025 - 02 - 19 - 第 55 篇
Author: 鄭龍浩 / 仟濹(CSND)
【二分搜索】
文章目錄
- 洛谷 P1873 EKO / 砍樹
- 題目描述
- 輸入格式
- 輸出格式
- 輸入輸出樣例 #1
- 輸入 #1
- 輸出 #1
- 輸入輸出樣例 #2
- 輸入 #2
- 輸出 #2
- 說明/提示
- 題目中的部分變量
- 思路
- 代碼
洛谷 P1873 EKO / 砍樹
題目描述
伐木工人 Mirko 需要砍 M M M 米長的木材。對 Mirko 來說這是很簡單的工作,因為他有一個漂亮的新伐木機,可以如野火一般砍伐森林。不過,Mirko 只被允許砍伐一排樹。
Mirko 的伐木機工作流程如下:Mirko 設置一個高度參數 H H H(米),伐木機升起一個巨大的鋸片到高度 H H H,并鋸掉所有樹比 H H H 高的部分(當然,樹木不高于 H H H 米的部分保持不變)。Mirko 就得到樹木被鋸下的部分。例如,如果一排樹的高度分別為 20 , 15 , 10 20,15,10 20,15,10 和 17 17 17,Mirko 把鋸片升到 15 15 15 米的高度,切割后樹木剩下的高度將是 15 , 15 , 10 15,15,10 15,15,10 和 15 15 15,而 Mirko 將從第 1 1 1 棵樹得到 5 5 5 米,從第 4 4 4 棵樹得到 2 2 2 米,共得到 7 7 7 米木材。
Mirko 非常關注生態保護,所以他不會砍掉過多的木材。這也是他盡可能高地設定伐木機鋸片的原因。請幫助 Mirko 找到伐木機鋸片的最大的整數高度 H H H,使得他能得到的木材至少為 M M M 米。換句話說,如果再升高 1 1 1 米,他將得不到 M M M 米木材。
輸入格式
第 1 1 1 行 2 2 2 個整數 N N N 和 M M M, N N N 表示樹木的數量, M M M 表示需要的木材總長度。
第 2 2 2 行 N N N 個整數表示每棵樹的高度。
輸出格式
1 1 1 個整數,表示鋸片的最高高度。
輸入輸出樣例 #1
輸入 #1
4 7
20 15 10 17
輸出 #1
15
輸入輸出樣例 #2
輸入 #2
5 20
4 42 40 26 46
輸出 #2
36
說明/提示
對于 100 % 100\% 100% 的測試數據, 1 ≤ N ≤ 1 0 6 1\le N\le10^6 1≤N≤106, 1 ≤ M ≤ 2 × 1 0 9 1\le M\le2\times10^9 1≤M≤2×109,樹的高度 ≤ 4 × 1 0 5 \le 4\times 10^5 ≤4×105,所有樹的高度總和 > M >M >M。
題目中的部分變量
tree_num - 樹木數量
trees[] - 每個樹木的高度
tree_sum - 需要的木材總長度
max_JHight - 鋸片的最高高度
max_TreeHight - 樹木最高高度
tree_NewSum - 存儲當前鋸片高度砍的樹木高度之和
思路
理一下思路,首先,剛開始是沒想到這個題怎么做的,我看到標簽寫的**“二分算法”**,然后往這方面想才有了思路。
首先,該題要求的是:max_hight
鋸片的最高高度,這個鋸片的高度肯定是有一個區間的,所以只需要在這個區間(1 ~ 最高的樹木高度
)內查找到一個符合條件的值即可。
即使用二分搜索法在區間
[ 1 —— max_TreeHight ]
內尋找符合**條件(下面寫了什么條件)**的值(或叫高度)
這個所謂條件就是:
鋸下來的所有木材之和 大于等于 需要的木材總長度
注意數據范圍
1≤N≤106,1≤M≤2×109,樹的高度 ≤4×105,所有樹的高度總和 >M。
所以
long long
還有一些細節問題
關于一些特殊情況以及細節,我已經在寫代碼的時候進行了標注和注釋
既然理清思路,就開始寫代碼了,代碼如下
代碼
//洛谷P1873 KEO/砍樹
// 2025-02-18
// 鄭龍浩 / 仟濹(CSDN)
// tree_num - 樹木數量
// trees[] - 每個樹木的高度
// tree_sum - 需要的木材總長度
// max_JHight - 鋸片的最高高度
// max_TreeHight - 樹木最高高度
// tree_NewSum - 存儲當前鋸片高度砍的樹木高度之和
#include <iostream>
#include <algorithm>
using namespace std;
// 二分搜索符合 鋸下來的所有木材之和 **大于等于** 需要的木材總長度 的高度
long long binary_searchSelf( long long trees[], long long tree_num, long long tree_sum, long long max_TreeHight ){long long left = 0, right = max_TreeHight, middle; // 左定位 右定位 中間定位long long tree_NewSum; // 存儲當前鋸片高度砍的樹木高度之和long long max_JHight = 0;// 尋找最合適高度(鋸片的)// 因為這個地方寫為 left < right 我找錯找了好久,要注意// 千萬不要寫成 left < right// 為什么left == right 的時候仍然要進入循環呢,因為此時的left 與 right還未曾參與計算// 需要進入循環將 middle 賦值為 (left + right) / 2while( left <= right ){tree_NewSum = 0;middle = (left + right) / 2;for( int i = 0; i < tree_num; i ++ ){// 如果電鋸高度保持在 樹木的高度內,鋸下來就是>0,累加;電鋸高度大于樹木高度,則是砍空氣,不計數(算出來是負數)// 即:如果可砍,則就累加if( middle < trees[ i ] )tree_NewSum += trees[ i ] - middle;}// 不斷尋找 當前鋸片高度砍的樹木高度之和 == 需要的木材總長度 的數值// 招不到就找 當前鋸片高度砍的樹木高度之和 > 需要的木材總長度 且 最接近的 數值if( tree_NewSum >= tree_sum ){// 當前鋸下來的總和 >= 需要的木材總和max_JHight = middle;left = middle + 1;} else{// 當前鋸下來的總和 < 需要的木材總和// 至于為什么在這不 max_JHight = middle 操作,是因為鋸下來的總和不滿足需要的木材right = middle - 1;}}return max_JHight; // 返回鋸片最高高度
}
int main( void ){long long tree_num, tree_sum; // 樹木數量 需要的木材總長度cin >> tree_num >> tree_sum; // 輸入 木數量 需要的木材總長度long long trees[ tree_num ]; // 每個樹木的高度long long max_TreeHight; // 樹木最高高度for( int i = 0; i < tree_num; i ++ ){cin >> trees[ i ];}sort( trees, trees + tree_num ); // 讓數據變為升序,以便使用 二分搜索法max_TreeHight = trees[tree_num - 1];cout << binary_searchSelf(trees, tree_num, tree_sum, max_TreeHight) << endl;return 0;
}