1488. 避免洪水泛滥
难度: 中等
来源: 每日一题 2023.10.13
你的国家有无数个湖泊,所有湖泊一开始都是空的。当第 n
个湖泊下雨前是空的,那么它就会装满水。如果第 n
个湖泊下雨前是 满的 ,这个湖泊会发生 洪水 。你的目标是避免任意一个湖泊发生洪水。
给你一个整数数组 rains ,其中:
rains[i] > 0
表示第 i 天时,第 rains[i] 个湖泊会下雨。rains[i] == 0
表示第 i 天没有湖泊会下雨,你可以选择 一个 湖泊并 抽干 这个湖泊的水。
请返回一个数组 ans
,满足:
ans.length == rains.length
- 如果
rains[i] > 0
,那么ans[i] == -1
。 - 如果
rains[i] == 0
,ans[i]
是你第i
天选择抽干的湖泊。 - 如果有多种可行解,请返回它们中的 任意一个 。如果没办法阻止洪水,请返回一个 空的数组 。
请注意,如果你选择抽干一个装满水的湖泊,它会变成一个空的湖泊。但如果你选择抽干一个空的湖泊,那么将无事发生。
示例 1:
输入:rains = [1,2,3,4]
输出:[-1,-1,-1,-1]
解释:第一天后,装满水的湖泊包括 [1]
第二天后,装满水的湖泊包括 [1,2]
第三天后,装满水的湖泊包括 [1,2,3]
第四天后,装满水的湖泊包括 [1,2,3,4]
没有哪一天你可以抽干任何湖泊的水,也没有湖泊会发生洪水。
示例 2:
输入:rains = [1,2,0,0,2,1]
输出:[-1,-1,2,1,-1,-1]
解释:第一天后,装满水的湖泊包括 [1]
第二天后,装满水的湖泊包括 [1,2]
第三天后,我们抽干湖泊 2 。所以剩下装满水的湖泊包括 [1]
第四天后,我们抽干湖泊 1 。所以暂时没有装满水的湖泊了。
第五天后,装满水的湖泊包括 [2]。
第六天后,装满水的湖泊包括 [1,2]。
可以看出,这个方案下不会有洪水发生。同时, [-1,-1,1,2,-1,-1] 也是另一个可行的没有洪水的方案。
示例 3:
输入:rains = [1,2,0,1,2]
输出:[]
解释:第二天后,装满水的湖泊包括 [1,2]。我们可以在第三天抽干一个湖泊的水。
但第三天后,湖泊 1 和 2 都会再次下雨,所以不管我们第三天抽干哪个湖泊的水,另一个湖泊都会发生洪水。
提示:
1 <= rains.length <= 10^5
0 <= rains[i] <= 10^9
class Solution {
public int[] avoidFlood(int[] rains) {
}
}
分析与题解
-
贪心算法
对于这个题目, 如果洪水泛滥, 那么在数据上会有怎么的规律呢?
洪水泛滥的数据规律:
对于第i个湖, 下雨 → 下雨 → 泛滥
对于第i个湖, 下雨 → 抽干 → 下雨 → 无事
也就说 对于第i个湖, 两次下雨之间一定要有一次能抽水的机会, 才能避免泛滥
所以对于能抽干湖的机会, 我们要保存下来它的所在天数(所在下标), 当后面某
i
天, 当某一个湖要泛滥时, 我们尝试往前找能抽干湖的机会. 找到了就万事大吉. 找不到就直接导致洪水泛滥, GG.另外有如图的情况,
红色的湖
和桔色的湖
都要泛滥了, 单独来看每一个湖都可以在两次机会中任选一个, 但是两者合在一起的时候,红色的湖
一定选择前面的抽干机会,桔色的湖
才能不会泛滥...接下来, 我们看一下具体的解题过程.
首先初始化各个空间, 其中
result
是结果数组,zeroIndexs
是存储的抽干湖的机会所在天数(所在下标),cache
表示湖的水量, key是湖的下标, value表示湖的水量.int[] result = new int[rains.length]; List<Integer> zeroIndexs = new ArrayList<>(); Map<Integer, Integer> cache = new HashMap<>();
然后我们遍历
rains
数组.在遍历过程中, 首先有这样的几种情况需要进行不同的逻辑分支.
-
当前有抽干湖的机会, 添加到
zeroIndexs
中.result[i] = 1;
则是初始化一个值,后期没人使用这次机会那就让这次机会抽第1
个湖.// 添加豁免权 if(rains[i] == 0) { zeroIndexs.add(i); result[i] = 1; continue; }
-
如果是正常下雨天数并且当前湖里没有水, 那就直接下雨填满湖就行.
// 正常流程 Integer value = cache.getOrDefault(rains[i], -1); if(value == -1) { // 没有水 cache.put(rains[i], i); result[i] = -1; }
-
如果是下雨天但是当前湖里有水, 我们就要从
zeroIndexs
找到合适的机会抽水, 如果找不到,那就洪水泛滥了...从前往后查找, 并且
下雨天下标
<抽水天下标
<下雨天下标
boolean isSuccess = false; for(int j = 0; j < zeroIndexs.size(); j++) { Integer index = zeroIndexs.get(j); if(index > value && index < i) { result[index] = rains[i]; zeroIndexs.remove(j); cache.put(rains[i], i); isSuccess = true; break; } } // 对于某一个湖 一定是 先下雨 抽水 再下雨这个流程, 而不能是 抽水 下雨 再下雨. // 这个逻辑通过判断先前的下雨的下标和最后一次豁免权的下标来的. if(!isSuccess) { // 没有豁免权在两次下雨中间,泛滥 return new int[0]; } result[i] = -1;
接下来, 我们一起看一下整体的解题过程.
class Solution { public int[] avoidFlood(int[] rains) { int[] result = new int[rains.length]; // 事件一共有三种 // 下雨, 抽水, 泛滥 // 0代表着豁免权, 也就是当某一个湖要泛滥时(>=2), 我们可以用在前面的某一个位置的0抽水,防止泛滥 // 如果zeroIndexs为空,则代表着没有能力阻止泛滥 List<Integer> zeroIndexs = new ArrayList<>(); // 创建一个字典, key是湖的下标, value是湖的水量==1的天数 Map<Integer, Integer> cache = new HashMap<>(); for(int i = 0; i < rains.length; i++) { // 添加豁免权 if(rains[i] == 0) { zeroIndexs.add(i); result[i] = 1; continue; } // 正常流程 Integer value = cache.getOrDefault(rains[i], -1); if(value == -1) { // 没有水 cache.put(rains[i], i); result[i] = -1; } else { boolean isSuccess = false; for(int j = 0; j < zeroIndexs.size(); j++) { Integer index = zeroIndexs.get(j); if(index > value && index < i) { result[index] = rains[i]; zeroIndexs.remove(j); cache.put(rains[i], i); isSuccess = true; break; } } // 对于某一个湖 一定是 先下雨 抽水 再下雨这个流程, 而不能是 抽水 下雨 再下雨. // 这个逻辑通过判断先前的下雨的下标和最后一次豁免权的下标来的. if(!isSuccess) { // 没有豁免权在两次下雨中间,泛滥 return new int[0]; } result[i] = -1; } } return result; } }
复杂度分析:
- 时间复杂度: O(nm) ,
n
是 天气情况数组的长度.m
是zeroIndexs
数组的长度. - 空间复杂度: O(2n),
zeroIndexs
、cache
的空间复杂度都与元素个数长度相关.
结果如下所示.
-
Comments | 0 条评论