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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
方法一:贪心算法
思路
判断能否在不打破种植规则的情况下在花坛内种入 nnn 朵花,从贪心的角度考虑,应该在不打破种植规则的情况下种入尽可能多的花,然后判断可以种入的花的最多数量是否大于或等于 nnn。
假设花坛的下标 iii 和下标 jjj 处都种植了花,其中 j−i≥2j-i \ge 2j−i≥2,且在下标 [i+1,j−1][i+1,j-1][i+1,j−1] 范围内没有种植花,则只有当 j−i≥4j-i \ge 4j−i≥4 时才可以在下标 iii 和下标 jjj 之间种植更多的花,且可以种植花的下标范围是 [i+2,j−2][i+2,j-2][i+2,j−2]。可以种植花的位置数是 p=j−i−3p=j-i-3p=j−i−3,当 ppp 是奇数时最多可以在该范围内种植 (p+1)/2(p+1)/2(p+1)/2 朵花,当 ppp 是偶数时最多可以在该范围内种植 p/2p/2p/2 朵花。由于当 ppp 是偶数时,在整数除法的规则下 p/2p/2p/2 和 (p+1)/2(p+1)/2(p+1)/2 相等,因此无论 ppp 是奇数还是偶数,都是最多可以在该范围内种植 (p+1)/2(p+1)/2(p+1)/2 朵花,即最多可以在该范围内种植 (j−i−2)/2(j-i-2)/2(j−i−2)/2 朵花。
上述情况是在已有的两朵花之间种植花的情况(已有的两朵花之间没有别的花)。假设花坛的下标 lll 处是最左边的已经种植的花,下标 rrr 处是最右边的已经种植的花(即对于任意 k<lk<lk<l 或 k>rk>rk>r 都有 flowerbed[k]=0\textit{flowerbed}[k]=0flowerbed[k]=0),如何计算在下标 lll 左边最多可以种植多少朵花以及在下标 rrr 右边最多可以种植多少朵花?
下标 lll 左边有 lll 个位置,当 l<2l<2l<2 时无法在下标 lll 左边种植花,当 l≥2l \ge 2l≥2 时可以在下标范围 [0,l−2][0,l-2][0,l−2] 范围内种植花,可以种植花的位置数是 l−1l-1l−1,最多可以种植 l/2l/2l/2 朵花。
令 mmm 为数组 flowerbed\textit{flowerbed}flowerbed 的长度,下标 rrr 右边有 m−r−1m-r-1m−r−1 个位置,可以种植花的位置数是 m−r−2m-r-2m−r−2,最多可以种植 (m−r−1)/2(m-r-1)/2(m−r−1)/2 朵花。
如果花坛上没有任何花朵,则有 mmm 个位置可以种植花,最多可以种植 (m+1)/2(m+1)/2(m+1)/2 朵花。
根据上述计算方法,计算花坛中可以种入的花的最多数量,判断是否大于或等于 nnn 即可。具体做法如下。
- 维护 prev\textit{prev}prev 表示上一朵已经种植的花的下标位置,初始时 prev=−1\textit{prev}=-1prev=−1,表示尚未遇到任何已经种植的花。
- 从左往右遍历数组 flowerbed\textit{flowerbed}flowerbed,当遇到 flowerbed[i]=1\textit{flowerbed}[i]=1flowerbed[i]=1 时根据 prev\textit{prev}prev 和 iii 的值计算上一个区间内可以种植花的最多数量,然后令 prev=i\textit{prev}=iprev=i,继续遍历数组 flowerbed\textit{flowerbed}flowerbed 剩下的元素。
- 遍历数组 flowerbed\textit{flowerbed}flowerbed 结束后,根据数组 prev\textit{prev}prev 和长度 mmm 的值计算最后一个区间内可以种植花的最多数量。
- 判断整个花坛内可以种入的花的最多数量是否大于或等于 nnn。
由于只需要判断能否在不打破种植规则的情况下在花坛内种入 nnn 朵花,不需要具体知道最多可以在花坛内种入多少朵花,因此可以在循环内进行优化,当可以种入的花的数量已经达到 nnn,则可以直接返回 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 相邻的两边之后必然是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;
}
};
复杂度分析
时间复杂度:O(m)O(m)O(m),其中 mmm 是数组 flowerbed\textit{flowerbed}flowerbed 的长度。需要遍历数组一次。
空间复杂度:O(1)O(1)O(1)。额外使用的空间为常数。
1689

被折叠的 条评论
为什么被折叠?



