制作 m 束花所需的最少天数(二分)

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

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

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

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

请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -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

解法一、二分+排序

天数具有二段性和排他性,假设答案天数是day : [0,day)的范围内都是不满足制作m束花的,(day,inf)的范围内都可以制作,但是day这个天数是可以确定的。 这就是二分的左边界查找day或右边界粗略查找来找day-1;

  1. 看题目中的解释, 找的是一个天数可以满足制作m束花,并加了一个限定要确定最少天数。就自然想到了在一个有序的天数序列二分左边界找符合制作m束花的天数。
  2. 检查函数就是遍历整个bloomDay遇到开花的就统计能制作多少束花,最后返回相应天数制作的总数。

二分左边界模板:

// 找值为target的处于最左位置的索引
    public int leftBound(int[] nums,int l, int r, int target){
        while(l<=r){
            int mid = l + (r-l)/2;
            if(nums[mid]>=target){
                r = mid-1;  //开区间
            }else{
                l = mid+1;//开区间
            }
        }
        // 越界或target不存在
        if(l>=nums.length || nums[l] != target)
            return -1;
        return l;
    }
class Solution {
int[] blooms;
  public int minDays(int[] bloomDay, int m, int k) {
    if(m*k > bloomDay.length) return -1; //需要的花多大于实际的花朵
    Set<Integer>  sortedDay = new TreeSet<Integer>((a,b) -> {return a-b;});
    for(int b : bloomDay){
        sortedDay.add(b);
    }
    Integer[] days =  (Integer[] ) sortedDay.toArray(new Integer[0]);
    //在days中二分查找合适的day
    if(m*k == bloomDay.length) return days[days.length-1].intValue(); //需要全部的花,在最大的日子
    blooms = bloomDay;
    return  leftBound(days, m, k);
  }
  /**
   *   左边界二分查找
   * @param days  排序过后的花开的日子
   * @param m  制作m束花
   * @param k  多少多花一束花
   * @return 最小的日子制作m束花
   */
    public int leftBound(Integer[] days, int m, int k)
    {
      int left = 0, right = days.length-1;
      while(left <= right){
        int mid = left + (right - left)/2;
        int num = check(days[mid], m, k); // mid这个日子里可以制作多少束花.
        //System.out.println(mid + " : " + num);
        if( num >= m){
          right = mid-1;
        }else{
          left = mid+1;
        }
      }
      if(left >= days.length) return -1;
      return days[left];
    }
    // 检查day天后可以制作花的数量
    public int check(int day, int m, int k){
        int i = 0, res = 0;
        while(i < blooms.length){
          if(blooms[i]<=day){
            //这朵花可以开 
            int tmp = 0;
            while(i < blooms.length && blooms[i] <= day){
                tmp++;
                if(tmp == k){
                  //连续开花达到制作一束花的水平
                  res++;
                  tmp = 0;
                }
                i++;
            }
        }
        ++i;
    }
    return res;
  }
}

时间复杂度: Treeset添加n个数是O(logn) , 二分是O(logn),检查函数是O(n), 总的时间复杂度是O(nlogn)???但是力扣跑出来比解法二慢。不晓得为什么。

空间复杂度:O(n)

解法二、对1-1e9的范围进行二分

class Solution {
int[] blooms;
  public int minDays(int[] bloomDay, int m, int k) {
     
    //在days中二分查找合适的day 
      if( m*k > bloomDay.length) return -1;
    blooms = bloomDay;
    return  leftBound( m, k);
  }
  /**
   *   左边界二分查找
   * @param m  制作m束花
   * @param k  多少多花一束花
   * @return 最小的日子制作m束花
   */
    public int leftBound( int m, int k)
    {
      int left = 0, right = (int)1e9;
      while(left <= right){
        int mid = left + (right - left)/2;
        int num = check(mid, m, k); // mid这个日子里可以制作多少束花.
        //System.out.println(mid + " : " + num);
        if( num >= m){
          right = mid-1;
        }else{
          left = mid+1;
        }
      } 
      return left;
    }
    public int check(int day, int m, int k){
        int i = 0, res = 0;
        while(i < blooms.length){
          if(blooms[i]<=day){
            //这朵花可以开 
            int tmp = 0;
            while(i < blooms.length && blooms[i] <= day){
                tmp++;
                if(tmp == k){
                  //连续开花达到制作一束花的水平
                  res++;
                  tmp = 0;
                }
                i++;
            }
        }
        ++i;
    }
    return res;
  }
}
  • 时间复杂度:check 函数的复杂度为 O(n)。整体复杂度为 O(nlog(1e9))
  • 空间复杂度:O(n)
优化:

对二分时的边界做优化,left=min{bloomday},right = max{bloomday}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值