洛谷題解——P1873:砍樹

題目相關

題目鏈接

洛谷,https://www.luogu.com.cn/problem/P1873

我的 OJ,http://47.110.135.197/problem.php?id=4960。區別在於數據集比洛谷的小。

題目描述

伐木工人米爾科需要砍倒M米長的木材。這是一個對米爾科來說很容易的工作,因爲他有一個漂亮的新伐木機,可以像野火一樣砍倒森林。不過,米爾科只被允許砍倒單行樹木。

米爾科的伐木機工作過程如下:米爾科設置一個高度參數H(米),伐木機升起一個巨大的鋸片到高度H,並鋸掉所有的樹比H高的部分(當然,樹木不高於H米的部分保持不變)。米爾科就行到樹木被鋸下的部分。

例如,如果一行樹的高度分別爲20,15,10和17,米爾科把鋸片升到15米的高度,切割後樹木剩下的高度將是15,15,10和15,而米爾科將從第1棵樹得到5米,從第4棵樹得到2米,共得到7米木材。

米爾科非常關注生態保護,所以他不會砍掉過多的木材。這正是他爲什麼儘可能高地設定伐木機鋸片的原因。幫助米爾科找到伐木機鋸片的最大的整數高度H,使得他能得到木材至少爲M米。換句話說,如果再升高1米,則他將得不到M米木材。

輸入格式

第1行:2個整數N和M,N表示樹木的數量(1<=N<=1000000),M表示需要的木材總長度(1<=M<=2000000000)

第2行:N個整數表示每棵樹的高度,值均不超過1000000000。所有木材長度之和大於M,因此必有解。

輸出格式

第1行:1個整數,表示砍樹的最高高度。

輸入樣例

5 20
4 42 40 26 46

輸出樣例

36

題目分析

題意分析

給了一個要獲得的樹木長度,要求最小的砍樹高度。因此我們可以考慮本題屬於查找算法,可以考慮使用二分查找。

樣例數據分析

樣例數據輸入爲:

5 20
4 42 40 26 46

意味這我們一共有 5 棵數目,數目的高度爲:4,42,40,26,46,砍樹後,我們要得到 20 米的長度。

下面我們來模擬一下這個二分查找過程。

首先我們知道二分查找的要素有以下幾個:

1、左邊界和右邊界。本題中,我們可以比較輕易的知道:左邊界爲 0,因爲題目告訴我們 M 的範圍最小值爲 1。右邊界應該是所有數目的最大高度。

2、check() 函數。就是用 mid 砍樹的高度,對樹木進行修剪,看得到的樹木長度。如果大於等於 M,說明 mid 夠,那麼我們應該擴大 mid。如果小於 M,說明 mid 不夠,那麼我們應該縮小 mid。

3、模板。我推薦的模板有兩個,一個模板用來找左下界,一個用來找右上界。從 check() 函數的描述中,我們可以看到,這是一個找右上界的過程。有人覺得奇怪,爲什麼?看題目我們就知道是一個找最大值的過程,所以必然是找右上界。

下面我們來一次完整的二分查找過程,如下表所示:

  left right mid 砍樹長度 結論
1 0 46 (0+46)/2=23 0+19+17+3+23=62  
2 32 46 (32+46)/2=35 0+7+5+0+11=23  
3 35 46 (35+46)/2=41 0+1+0+0+5=6  
4 35 40 (35+40)/2=38 0+4+2+0+8=14  
5 35 37 (35+37)/2=36 0+6+4+0+10=20  
6 36 37 (36+37)/2=37 0+5+3+0+9=17  
7 36 36      

數據規模分析

1、N 的範圍爲 1e6。

2、M 的範圍爲 2e9。

3、每顆樹的高度不超過 1e9。

從上面的描述我們可以比較清楚的看到 int 是足夠描述數據的。

算法思路

套用我推薦的二分查找模板二即可,具體參考這個鏈接,https://blog.csdn.net/justidle/article/details/104527596

AC 參考代碼

考慮到數據有 1e6 個大小,因此只能使用 scanf/printf 或者快讀。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1e6+2;
int nums[MAXN];//樹的高度
int n, m;

bool check(int value) {
    int ans = 0;
    for (int i=0; i<n; i++) {
        if (nums[i]>=value) {
            ans += (nums[i]-value);
        }

        if (ans >= m) {
            return true;
        }
    }

    return ans>=m ? true : false;
}

int bsearch(int left, int right) {
    int mid;

    while (left < right) {
        mid = left + ((right-left+1)>>1);
        if (check(mid)) {
            left = mid;
        } else {
            right = mid-1;            
        }
    }

    return left;
}

int main() {
    scanf("%d%d", &n, &m);
    int left=0, right=0;
    for (int i=0; i<n; i++) {
        scanf("%d", &nums[i]);
        right = max(right, nums[i]);
    }

    //二分查找
    printf("%d\n", bsearch(left, right));

    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章