• 1488. 避免洪水泛滥



    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]
    没有哪一天你可以抽干任何湖泊的水,也没有湖泊会发生洪水。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 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] 也是另一个可行的没有洪水的方案。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例 3:

    输入:rains = [1,2,0,1,2]
    输出:[]
    解释:第二天后,装满水的湖泊包括 [1,2]。我们可以在第三天抽干一个湖泊的水。
    但第三天后,湖泊 1 和 2 都会再次下雨,所以不管我们第三天抽干哪个湖泊的水,另一个湖泊都会发生洪水。
    
    • 1
    • 2
    • 3
    • 4

    提示:

    • 1 <= rains.length <= 10^5
    • 0 <= rains[i] <= 10^9
    class Solution {
        public int[] avoidFlood(int[] rains) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    分析与题解

    • 贪心算法

      对于这个题目, 如果洪水泛滥, 那么在数据上会有怎么的规律呢?

      洪水泛滥的数据规律:

      对于第i个湖, 下雨 → 下雨 → 泛滥

      对于第i个湖, 下雨 → 抽干 → 下雨 → 无事

      也就说 对于第i个湖, 两次下雨之间一定要有一次能抽水的机会, 才能避免泛滥

      所以对于能抽干湖的机会, 我们要保存下来它的所在天数(所在下标), 当后面某 i 天, 当某一个湖要泛滥时, 我们尝试往前找能抽干湖的机会. 找到了就万事大吉. 找不到就直接导致洪水泛滥, GG.

      leetcode1488_3-2023-10-13-15-01-18

      另外有如图的情况, 红色的湖桔色的湖 都要泛滥了, 单独来看每一个湖都可以在两次机会中任选一个, 但是两者合在一起的时候, 红色的湖 一定选择前面的抽干机会, 桔色的湖才能不会泛滥…

      接下来, 我们看一下具体的解题过程.

      首先初始化各个空间, 其中 result 是结果数组, zeroIndexs 是存储的抽干湖的机会所在天数(所在下标), cache 表示湖的水量, key是湖的下标, value表示湖的水量.

      int[] result = new int[rains.length];
      List zeroIndexs = new ArrayList<>();
      Map cache = new HashMap<>();
      
      • 1
      • 2
      • 3

      然后我们遍历 rains 数组.

      在遍历过程中, 首先有这样的几种情况需要进行不同的逻辑分支.

      • 当前有抽干湖的机会, 添加到 zeroIndexs 中. result[i] = 1; 则是初始化一个值,后期没人使用这次机会那就让这次机会抽第 1 个湖.

        // 添加豁免权
        if(rains[i] == 0) {
            zeroIndexs.add(i);
            result[i] = 1;
            continue;
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
      • 如果是正常下雨天数并且当前湖里没有水, 那就直接下雨填满湖就行.

        // 正常流程
        Integer value = cache.getOrDefault(rains[i], -1);
        if(value == -1) {
            // 没有水
            cache.put(rains[i], i);
            result[i] = -1;
        }       
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • 如果是下雨天但是当前湖里有水, 我们就要从 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;
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18

      接下来, 我们一起看一下整体的解题过程.

      class Solution {
          public int[] avoidFlood(int[] rains) {
              int[] result = new int[rains.length];
              // 事件一共有三种
              // 下雨, 抽水, 泛滥
      
              // 0代表着豁免权, 也就是当某一个湖要泛滥时(>=2), 我们可以用在前面的某一个位置的0抽水,防止泛滥
              // 如果zeroIndexs为空,则代表着没有能力阻止泛滥
              List zeroIndexs = new ArrayList<>();
              // 创建一个字典, key是湖的下标, value是湖的水量==1的天数
              Map 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;
          }
      }
      
      • 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
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49

      复杂度分析:

      • 时间复杂度: O(nm) , n 是 天气情况数组的长度. mzeroIndexs 数组的长度.
      • 空间复杂度: O(2n), zeroIndexscache 的空间复杂度都与元素个数长度相关.

      结果如下所示.

  • 相关阅读:
    JS 字符串转 GBK 编码超精简实现
    万字详解数据仓库、数据湖、数据中台和湖仓一体
    软件加密系统Themida应用程序保护指南(八):额外的选择
    北邮22级信通院数电:Verilog-FPGA(5)第四第五周实验 密码保险箱的设计
    Python模拟登录豆瓣:轻松探索海量文化资源!
    以sqlilabs靶场为例,讲解SQL注入攻击原理【15-17关】
    Go-Excelize API源码阅读(三十三)—— RemoveCol
    宝塔面板建站
    疫情物资储藏库建设规划问题,使用matlab+cplex+yalmib求解
    CronJob in K8s
  • 原文地址:https://blog.csdn.net/qq_33591200/article/details/133814330