P4058 [Code+#1] 木材 二分答案

解题思路

这道题目要求我们找到最少需要等待多少个月,才能收集到足够长度和数量的木材。主要思路是使用二分查找来确定最小满足条件的月份。

关键点分析:

  1. 二分查找的应用:我们需要找到最小的月份mid,使得在这个月份之后,所有满足高度条件的树的总和≥S。二分查找可以高效地缩小搜索范围。

  2. 检查函数check(mid):对于给定的月份mid,计算每棵树的高度(初始高度 + 每月生长高度 × 月份数),并累加满足高度≥L的树的高度,判断总和是否≥S。

  3. 边界处理:特别处理0个月的情况,即初始状态是否已经满足条件。

时间复杂度分析:

  • 二分查找的时间复杂度是O(log(max_months)),其中max_months是可能的最大月份,这里是1e18。

  • 每次check(mid)的时间复杂度是O(n),因为需要遍历所有树。

  • 总的时间复杂度是O(n log(max_months)),对于n=2e5来说是可行的。

代码注释

#include<bits/stdc++.h>
#define ll unsigned long long // 使用unsigned long long防止乘法溢出
using namespace std;
const int N = 2e5 + 10;
ll n, s, l; // n:树的数量,s:订单总量,l:单块木料的最小长度
ll a[N], h[N]; // a[i]:第i棵树每月生长高度,h[i]:第i棵树的初始高度

// 检查函数:判断在mid个月后是否能满足订单需求
bool check(ll mid)
{
    ll sum = 0; // 累计满足条件的树木总高度
    for(int i = 1; i <= n; i++)
    {
        // 如果当前树在mid个月后的高度≥L,则计入总和
        if(h[i] + mid * a[i] >= l){
            sum += h[i] + mid * a[i];
        }
        // 如果已经满足订单需求,提前退出循环
        if(sum >= s) break;
    }
    // 返回是否满足订单需求
    return sum >= s;
}

int main()
{
    // 输入数据
    cin >> n >> s >> l;
    for(int i = 1; i <= n; i++) scanf("%lld",&h[i]);
    for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
    
    ll L = 0, R = 1e18, ans; // 初始化二分查找的左右边界
    
    // 特判0个月是否满足条件
    if(check(0)){
        cout << 0;
        return 0;
    }
    
    // 二分查找
    while(L <= R)
    {
        ll mid = (L + R) / 2;
        if(check(mid)) // 如果mid个月满足条件,尝试寻找更小的月份
        {
            ans = mid;
            R = mid - 1;
        } 
        else // 否则增加月份
        {
            L = mid + 1;
        }
    }
    cout << ans; // 输出最小满足条件的月份
    return 0;
}

 

posted @ 2025-05-30 17:03  CRt0729  阅读(15)  评论(0)    收藏  举报