• Leetcode(605)——种花问题


    Leetcode(605)——种花问题

    题目

    假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

    给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

    示例 1:

    输入:flowerbed = [1,0,0,0,1], n = 1
    输出:true
    示例 2:

    输入:flowerbed = [1,0,0,0,1], n = 2
    输出:false

    提示:

    1 <= flowerbed.length <= 2 * 104
    flowerbed[i] 为 0 或 1
    flowerbed 中不存在相邻的两朵花
    0 <= n <= flowerbed.length

    来源:力扣(LeetCode)
    链接:https://leetcode.cn/problems/can-place-flowers
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    题解

    方法一:贪心算法

    思路

    ​​  判断能否在不打破种植规则的情况下在花坛内种入 n n n 朵花,从贪心的角度考虑,应该在不打破种植规则的情况下种入尽可能多的花,然后判断可以种入的花的最多数量是否大于或等于 n n n

    ​​  假设花坛的下标 i i i 和下标 j j j 处都种植了花,其中 j − i ≥ 2 j-i \ge 2 ji2,且在下标 [ i + 1 , j − 1 ] [i+1,j-1] [i+1,j1] 范围内没有种植花,则只有当 j − i ≥ 4 j-i \ge 4 ji4 时才可以在下标 i i i 和下标 j j j 之间种植更多的花,且可以种植花的下标范围是 [ i + 2 , j − 2 ] [i+2,j-2] [i+2,j2]。可以种植花的位置数是 p = j − i − 3 p=j-i-3 p=ji3,当 p p p 是奇数时最多可以在该范围内种植 ( p + 1 ) / 2 (p+1)/2 (p+1)/2 朵花,当 p p p 是偶数时最多可以在该范围内种植 p / 2 p/2 p/2 朵花。由于当 p p p 是偶数时,在整数除法的规则下 p / 2 p/2 p/2 ( p + 1 ) / 2 (p+1)/2 (p+1)/2 相等,因此无论 p p p 是奇数还是偶数,都是最多可以在该范围内种植 ( p + 1 ) / 2 (p+1)/2 (p+1)/2 朵花,即最多可以在该范围内种植 ( j − i − 2 ) / 2 (j-i-2)/2 (ji2)/2 朵花。

    ​​  上述情况是在已有的两朵花之间种植花的情况(已有的两朵花之间没有别的花)。假设花坛的下标 l l l 处是最左边的已经种植的花,下标 r r r 处是最右边的已经种植的花(即对于任意 k < l k<l k<l k > r k>r k>r 都有 flowerbed [ k ] = 0 \textit{flowerbed}[k]=0 flowerbed[k]=0),如何计算在下标 l l l 左边最多可以种植多少朵花以及在下标 r r r 右边最多可以种植多少朵花?

    ​​  下标 l l l 左边有 l l l 个位置,当 l < 2 l<2 l<2 时无法在下标 l l l 左边种植花,当 l ≥ 2 l \ge 2 l2 时可以在下标范围 [ 0 , l − 2 ] [0,l-2] [0,l2] 范围内种植花,可以种植花的位置数是 l − 1 l-1 l1,最多可以种植 l / 2 l/2 l/2 朵花。

    ​​  令 m m m 为数组 flowerbed \textit{flowerbed} flowerbed 的长度,下标 r r r 右边有 m − r − 1 m-r-1 mr1 个位置,可以种植花的位置数是 m − r − 2 m-r-2 mr2,最多可以种植 ( m − r − 1 ) / 2 (m-r-1)/2 (mr1)/2 朵花。

    ​​  如果花坛上没有任何花朵,则有 m m m 个位置可以种植花,最多可以种植 ( m + 1 ) / 2 (m+1)/2 (m+1)/2 朵花。

    根据上述计算方法,计算花坛中可以种入的花的最多数量,判断是否大于或等于 n n n 即可。具体做法如下。

    • 维护 prev \textit{prev} prev 表示上一朵已经种植的花的下标位置,初始时 prev = − 1 \textit{prev}=-1 prev=1,表示尚未遇到任何已经种植的花。
    • 从左往右遍历数组 flowerbed \textit{flowerbed} flowerbed,当遇到 flowerbed [ i ] = 1 \textit{flowerbed}[i]=1 flowerbed[i]=1 时根据 prev \textit{prev} prev i i i 的值计算上一个区间内可以种植花的最多数量,然后令 prev = i \textit{prev}=i prev=i,继续遍历数组 flowerbed \textit{flowerbed} flowerbed 剩下的元素。
    • 遍历数组 flowerbed \textit{flowerbed} flowerbed 结束后,根据数组 prev \textit{prev} prev 和长度 m m m 的值计算最后一个区间内可以种植花的最多数量。
    • 判断整个花坛内可以种入的花的最多数量是否大于或等于 n n n

    ​​  由于只需要判断能否在不打破种植规则的情况下在花坛内种入 n n n 朵花,不需要具体知道最多可以在花坛内种入多少朵花,因此可以在循环内进行优化,当可以种入的花的数量已经达到 n n n,则可以直接返回 true \text{true} true,不需要继续计算数组剩下的部分。

    代码实现

    我的:

    class Solution {
    public:
        bool canPlaceFlowers(vector<int>& flowerbed, int n) {
            if(n == 0) return true;
            int size = flowerbed.size();
            int m = 0;
            int lastflower = -2;    // 为了处理 [0] 1 的特殊情况
            for(int i = 0 ; i < size; i++){
                if(flowerbed[i] == 1){
                    if(lastflower == -1) m += i/2;  // (i-0)/2
                    else m += (i-lastflower-2) > 0? (i-lastflower-2)/2: 0;
                    if(m >= n) return true;
                    lastflower = i;
                    i++;    // 跳过旁边的0
                }else if(i == size-1){
                    // 特殊情况即 [0,1,0,0,0]
                    // 为了处理 [0] 1 的特殊情况,令 lastflower = -2
                    m += (i-lastflower)/2;
                    if(m >= n) return true;
                }
            }
            return false;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    网友一个很巧妙的思路:
    只判断后面的位置有没有种花就够了。这利用 1 相邻的两边之后必然是0,和处于末尾的特性。
    因为如果 flowerbed[i]==1 的时候,它 i++ 了,循环一次后也会 i++,相当于 i 跳了两步,所以就不用判断前面的。

    class Solution {
    public:
        bool canPlaceFlowers(vector<int>& flowerbed, int n) {
            int size = flowerbed.size();
            for(int i = 0; i < size; ++i){
                if(flowerbed[i] == 0 && (i+1 == size || flowerbed[i+1] == 0)){
                    n--;
                    i++;
                }else if(flowerbed[i] == 1) i++;
                if(n <= 0) return true; // 小于0是为了避免n初始为0的情况 
            }
            return false;
        }
    };
    
    // 下面这个是我写的
    class Solution {
    public:
        bool canPlaceFlowers(vector<int>& flowerbed, int n) {
            int size = flowerbed.size();
            // 假定它的左相邻为0,则只需要检测其和右边是否为0即可
            // 然后再照顾一下末尾的特殊情况即可
            for(int i = 0; i < size; i++){
                if(flowerbed[i] == 1) i++;  // 跳转到相邻0的下一位,就可以确保下次检查的位置的左边为0
                else if(((i == size-1 && flowerbed[i] == 0)) || (flowerbed[i] == flowerbed[i+1])){
                    i++;
                    n--;
                }
                if(n <= 0) return true;
            }
            return false;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    复杂度分析

    时间复杂度 O ( m ) O(m) O(m),其中 m m m 是数组 flowerbed \textit{flowerbed} flowerbed 的长度。需要遍历数组一次。
    空间复杂度 O ( 1 ) O(1) O(1)。额外使用的空间为常数。

  • 相关阅读:
    LabelImg使用笔记
    【Java 进阶篇】Java Tomcat 入门指南
    【Java项目】瑞吉外卖保姆级学习笔记(改项目名称+改邮件验证码登录+功能补充)
    RHEL 8.6 Kubespray 1.23.0 install kubernetes v1.27.5
    【优化算法】凌日搜索算法【含Matlab源码 2150期】
    根据端口号查看进程
    Navicat for MySQL 11软件下载及安装教程
    flask框架添加异步任务处理模型任务
    visual studio2019怎么修改字体
    硅谷课堂笔记
  • 原文地址:https://blog.csdn.net/KCDCY/article/details/125457786