刷题的日常-制作 m 束花所需的最少天数

94 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

刷题的日常

一天一题,保持脑子清爽

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

题来自leetcode的1482题,题意如下:

给你一个整数数组 bloomDay,以及两个整数 m 和 k 。
现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。
花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。
请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。

理解题意

通过对题目的理解,可以将信息整理如下:

  • bloomDay是一个数组,存放了每朵花开花所需要的天数
  • m是需要制作的数量,要制作一束花,则必须摘k朵相邻的花,所以我们总共需要m * k朵花
  • 在满足m * k朵的条件下,要让开花的时间尽可能短
  • 返回最短的可以制作m束花的时间

做题思路

解题的步骤如下:

  • 首先,如果数组长度不满k * m,那么可以直接返回-1,因为不够数量
  • 如果够数量,那么最大天数肯定是可以制作出m束花,我们可以将最大值提取出来
  • 有了最大数量,我们就可以从 1到最大天数之间进行判断(假设天数为i),看下i天能否种出k个相邻的m束花
  • 暴力枚举显然不行,最大天数可能很大,那么我们可以用二分对天数进行搜索
    • 如果当前天数满足条件,那么可以往天数少的地方靠
    • 如果不满足,那么就往天数大的一侧靠
    • 返回最终结果

代码实现

对最大天数i来说,可能需要判断logi次,判断需要扫描数组,所以时间复杂度为O(nlogi),代码如下:

public class Solution {
    public int minDays(int[] bloomDay, int m, int k) {
        int total = m * k;
        if (total > bloomDay.length) {
            return -1;
        }
        int min = bloomDay[0];
        int max = bloomDay[0];
        for (int day : bloomDay) {
            max = Math.max(day, max);
            min = Math.min(day, min);
        }
        int l = min, r = max, mid;
        while (l < r) {
            mid = l + (r - l) / 2;
            if (check(bloomDay, mid, total, k)) {
                r = mid;
                continue;
            }
            l = mid + 1;
        }
        return l;
    }
    private boolean check(int[] bloomDay, int selDay, int total, int k) {
        int sum = 0;
        for (int day : bloomDay) {
            if (total <= 0) {
                return true;
            }
            if (selDay < day) {
                sum = 0;
                continue;
            }
            if (++sum % k == 0) {
                total -= k;
            }
        }
        return total <= 0;
    }
}

image.png