leetcode
leetcode 1351 ~ 1400
避免洪水泛滥

避免洪水泛滥

难度:

标签:

题目描述

代码结果

运行时间: 113 ms, 内存: 32.4 MB


/*
题目思路:
1. 使用一个哈希集合记录每个湖泊是否装满水。
2. 使用一个优先队列来存储需要抽干的湖泊,并且优先级最高的先被抽干。
3. 遍历rains数组:
   - 如果rains[i] > 0,表示湖泊下雨:
     - 如果湖泊已经满了,返回空数组表示无法避免洪水。
     - 否则,将湖泊标记为满。
   - 如果rains[i] == 0,表示可以选择一个湖泊抽干:
     - 如果有需要抽干的湖泊,则将其从集合中移除,并标记该湖泊为抽干。
4. 返回结果数组。*/

import java.util.*;
import java.util.stream.*;

public class Solution {
    public int[] avoidFlood(int[] rains) {
        Map<Integer, Integer> lakeMap = new HashMap<>(); // 记录每个湖泊上次下雨的天数
        TreeSet<Integer> dryDays = new TreeSet<>(); // 记录可以抽干的天数
        int[] ans = new int[rains.length];
        Arrays.fill(ans, -1);

        IntStream.range(0, rains.length).forEach(i -> {
            if (rains[i] > 0) { // 下雨
                if (lakeMap.containsKey(rains[i])) { // 该湖泊已满
                    Integer dryDay = dryDays.higher(lakeMap.get(rains[i])); // 找到最近的可抽干天数
                    if (dryDay == null) {
                        ans[0] = 0; // 标记为无法避免洪水
                        return;
                    }
                    ans[dryDay] = rains[i]; // 在dryDay那天抽干这个湖泊
                    dryDays.remove(dryDay); // 移除这个抽干的天数
                }
                lakeMap.put(rains[i], i); // 记录下雨的天数
            } else { // 不下雨,可以选择抽干湖泊
                dryDays.add(i); // 记录这天可以抽干
                ans[i] = 1; // 默认值,稍后会更新
            }
        });

        if (ans[0] == 0) return new int[0]; // 无法避免洪水

        dryDays.forEach(day -> ans[day] = 1); // 如果还有未使用的抽干天数,任意选择一个湖泊抽干

        return ans;
    }
}

解释

方法:

此题解的核心思路是使用散列表(字典)来跟踪当前哪些湖泊已经满了,以及它们最后一次被填满的时间。此外,使用一个双端队列(deque)来记录可以抽水的日子,并在需要时找到最近的、合适的日子来抽水,以避免洪水。遍历输入数组,对于每一天:1) 如果是晴天(rains[i] == 0),记录这一天可以抽水;2) 如果是下雨天(rains[i] > 0),检查该湖泊是否已经满了。如果已满,则需要在之前记录的可以抽水的日子中找到一个合适的日子来抽水;如果找不到合适的日子,直接返回空数组。如果湖泊未满,则标记为已满,并记录其下雨的时间。最后,对于所有未使用的晴天,可以随意抽干任何湖泊(避免特殊情况,通常抽干湖泊1)。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在实现中,你是如何确保找到的抽水日子是在该湖泊最后一次下雨之后的?能否详细说明这一逻辑?
在算法中,我们使用一个字典 `full_pools` 来记录每个湖泊最后一次下雨的日期。当湖泊再次下雨时,我们检查 `full_pools` 字典来确认湖泊是否已满。如果已满,则需要在 `can_removed` 双端队列中找到一个晴天来抽水。我们遍历 `can_removed`,寻找一个未被使用(`entry[1] == True`)且日期大于 `full_pools[p]` 的晴天(`entry[0] > full_pools[p]`)。这样确保了选定的抽水日子在该湖泊最后一次下雨之后,避免了在湖泊尚未满时抽水的逻辑错误。
🦆
题解中提到,如果找不到合适的晴天来抽水就返回空数组。这种情况是如何被触发的?能否举一个具体的例子说明?
这种情况发生在当一个湖泊在之前已经被填满,并且在该湖泊再次下雨之前没有任何有效的晴天来进行抽水时。例如,考虑输入数组 `rains = [1, 2, 1]`。第一天和第三天湖泊1下雨,而第二天湖泊2下雨。在第三天需要抽干湖泊1才能再次接受雨水,但由于之前没有晴天记录,没有可用的日子来抽水,因此返回空数组。
🦆
为什么在处理剩余的晴天时,选择默认抽干湖泊1,而不是其他湖泊?这里的选择对算法的结果有何影响?
在处理剩余的晴天时,选择默认抽干湖泊1主要是为了简化实现和确保算法的正确性,因为如果不指定抽水的湖泊,将存在多种可能的输出结果。选择抽干哪个湖泊在这种情况下不会影响算法的正确性,因为这些晴天是在所有必要的抽水操作之后的额外日子,此时所有需要抽水的湖泊都已处理完毕。因此,选择任何一个不存在或不需要抽水的湖泊都是有效的,这里选择湖泊1主要是为了保持输出的一致性。

相关问题