【二分查找】leetcode 1482. 制作 m 束花所需的最少天数

1482. 制作 m 束花所需的最少天数

题目描述

给你一个整数数组 bloomDay,以及两个整数 m 和 k 。

现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。

花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。

请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1

示例1:

输入: bloomDay = [1,10,3,10,2], m = 3, k = 1
输出: 3
解释: 让我们一起观察这三天的花开过程,x 表示花开,而 _ 表示花还未开。
现在需要制作 3 束花,每束只需要 1 朵。
1 天后:[x, _, _, _, _] // 只能制作 1 束花
2 天后:[x, _, _, _, x] // 只能制作 2 束花
3 天后:[x, _, x, _, x] // 可以制作 3 束花,答案为 3

示例2:

输入: bloomDay = [1,10,3,10,2], m = 3, k = 2
输出: -1
解释: 要制作 3 束花,每束需要 2 朵花,也就是一共需要 6 朵花。而花园中只有 5 朵花,无法满足制作要求,返回 -1 。

示例3:

输入: bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3
输出: 12
解释: 要制作 2 束花,每束需要 3 朵。
花园在 7 天后和 12 天后的情况如下:
7 天后:[x, x, x, x, _, x, x]
可以用前 3 朵盛开的花制作第一束花。但不能使用后 3 朵盛开的花,因为它们不相邻。
12 天后:[x, x, x, x, x, x, x]
显然,我们可以用不同的方式制作两束花。

示例4:

输入: bloomDay = [1000000000,1000000000], m = 1, k = 1
输出: 1000000000
解释: 需要等 1000000000 天才能采到花来制作花束

示例5:

输入: bloomDay = [1,10,2,9,3,8,4,7,5,6], m = 4, k = 2
输出: 9

提示

  • b l o o m D a y . l e n g t h = = n bloomDay.length == n bloomDay.length==n
  • 1 < = n < = 1 0 5 1 <= n <= 10^5 1<=n<=105
  • 1 < = b l o o m D a y [ i ] < = 1 0 9 1 <= bloomDay[i] <= 10^9 1<=bloomDay[i]<=109
  • 1 < = m < = 1 0 6 1 <= m <= 10^6 1<=m<=106
  • 1 < = k < = n 1 <= k <= n 1<=k<=n

方法:二分查找

解题思路

由题意得,当需要的天数越多时,则制作的花束就越少;当需要的天数越少时,则制作的花束就越多。因此,本题具有单调性,可以利用二分查找来解题。

首先我们把特别情况直接排除在外:一共要制作 m m m 束花,每束花需要 k k k 朵,如果 m ∗ k > b l o o m D a y . l e n g t h m * k > bloomDay.length mk>bloomDay.length,则无法满足制作要求,返回 -1。

二分查找的左边界为数组中的最小值,右边界为数组中的最大值,因为要保证制作出来的花束的数量。

判断 m i d mid mid 是否符合条件需要辅助函数 c h e c k check check
使用两个指针 i i i j j j 遍历数组, i i i j j j 指针一前一后构成了滑动窗口,先定位两个指针,然后判断 [ i , j ] [i,j] [i,j] 区间内的元素是否小于等于 m i d mid mid

  • 如果区间内有一个 b l o o m D a y [ i ] > m i d bloomDay[i] > mid bloomDay[i]>mid,则 i i i 指针往后移 1 位,对应的 j j j 指针也相对于 i i i 指针往后移 k − 1 k - 1 k1 位;
  • 如果区间内所有 b l o o m D a y [ i ] < = m i d bloomDay[i] <=mid bloomDay[i]<=mid,则 i i i 指针移到 j j j 指针,对应的 j j j 指针也向后移动 k k k 位,此时说明已成功制作 1 束花, c n t cnt cnt 加上 1。

最后,辅助函数 c h e c k check check 计算出在当前 m i d mid mid 天的情况下能制作出的总花数 c n t cnt cnt,如果 c n t > = m cnt >= m cnt>=m,则将二分查找区间缩小为 [ l , m i d ] [l,mid] [l,mid];如果 c n t < m cnt < m cnt<m,则将二分查找区间缩小为 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]

l l l r r r 相等时,二分查找结束,此时的 l l l 就是最少天数。

代码

class Solution {
public:
    bool check(vector<int>& bloomDay, int mid, int m, int k) {
        int cnt = 0;
        int n = bloomDay.size();
        for(int i = 0, j = k - 1; i < n && j < n; )
        {
            while(i <= j)
            {
                if(bloomDay[i] > mid)
                  break;
                i++;
            }
            if(i > j)   i = j, j += k, cnt++;
            else    j = i + k, i++;
        }
        return cnt >= m;
    }
    int minDays(vector<int>& bloomDay, int m, int k) {
        int n = bloomDay.size();
        if(m * k > n)   return -1;
        int l = *min_element(bloomDay.begin(), bloomDay.end()), 
        r = *max_element(bloomDay.begin(), bloomDay.end()), mid;
        while(l < r)
        {
            mid = (l + r) >> 1;
            if(check(bloomDay, mid, m, k))
                r = mid;
            else
                l = mid + 1;
        }
        return l;
    }
};

复杂度分析

  • 时间复杂度: O ( n l o g C ) O(nlogC) O(nlogC) C C C 为数组元素中的最大值与最小值的差。二分查找的时间复杂度为 O ( l o g C ) O(logC) O(logC),每一轮都需要遍历数组的时间复杂度为 O ( n ) O(n) O(n)。因此,整体时间复杂度为 O ( n l o g C ) O(nlogC) O(nlogC)
  • 空间复杂度: O ( 1 ) O(1) O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值