LeetCode 🟡 1488. Avoid Flood in The City
🔗 🟡 1488. Avoid Flood in The City
Problem Statement
題目簡述
給定一個長度為 的整數陣列 rains,其中:
rains[i] > 0表示第 天在湖泊rains[i]下雨。如果此時該湖泊已經是滿的,就會發生洪水。rains[i] == 0表示第 天是晴天,你可以選擇任意一個滿的湖泊將其抽乾(使其變為空)。
請回傳一個長度為 的答案陣列 ans:
- 若第 天下雨,
ans[i] = -1。 - 若第 天是晴天,
ans[i]為你選擇抽乾的湖泊編號(若沒有湖泊需要抽乾,可填入任意正整數如1)。 - 若無法避免洪水,回傳空陣列
[]。
Constraints
約束條件
思路:貪心策略與可用時間維護
本題是一道非常經典的 貪心 + 有序集合二分 或 區間併查集 題目。核心在於如何合理分配「晴天」這項寶貴資源,來化解未來可能發生的洪水危機。
核心貪心策略
假設某個湖泊在第 天下雨,接著在更晚的第 天再次下雨。為了防止這個湖泊溢出,我們必須在區間 之間的某個晴天,將該湖泊抽乾。
如果這段區間內有多個晴天,我們應該選擇哪一個?
- 貪心原則:我們應該選擇大於 的 第一個(最左邊的) 可用晴天。
- 直覺解釋:越早使用這個晴天資源,對未來的決策越有利。如果我們把這個湖泊的抽水時間往後拖延,可能會佔用到其他更晚下雨、但急需抽乾的湖泊的晴天資源。
方法一:有序容器上二分
根據貪心策略,問題可以轉化為:當我們發現某個湖泊在今天(第 天)再次下雨時,我們需要在所有「尚未被佔用的晴天」中,尋找第一個大於上次下雨天 的晴天。
核心想法是利用雜湊表記錄每個湖泊最後一次下雨的時間,並以有序容器(如 SortedList)動態維護所有還沒被使用的晴天日期。
遍歷每一天,若是晴天則先記錄下來;若某個湖泊再次下雨,我們就去有序容器中二分搜尋第一個大於該湖泊「上次下雨時間」的晴天。如果能找到,就在該晴天抽乾它,並將該晴天從容器中移除;若找不到,說明無法避免洪水,直接回傳空陣列。
當我們需要在一堆「動態產生且可被消耗的資源」中,尋找並使用「大於某個門檻值的最小資源」時,有序容器 + 二分搜尋是極為通用的標準模板。
複雜度分析
- 時間複雜度:。我們需要遍歷長度為 的陣列。每次遇到重複下雨時,在有序容器中二分搜尋並刪除元素需要 的時間。
- 空間複雜度:。我們需要雜湊表記錄湖泊的最後下雨日期,以及有序容器儲存晴天的日期,最多佔用 的空間。
方法二:區間併查集
為了避免二分搜尋的 開銷,我們可以用區間併查集將尋找晴天的複雜度優化到接近 。
相比於普通的併查集,將合併時改成向右合併,便可以用來維護區間,使得 表示自第 天起第一個可用於抽水的晴天位置。
當湖泊在上次下雨時間 之後需要被抽乾時,我們可以直接透過 瞬間定位到最左邊的可用晴天 :
- 若 ,說明兩次下雨之間沒有可用的晴天,無法避免洪水。
- 否則,在第 天抽水,並透過
union(j, j + 1)將其標記為已使用。此外,今天(下雨天)也不能被其他湖泊佔用,故同樣執行union(i, i + 1)。
併查集的 find 操作具有路徑壓縮的特性。當我們把已使用的位置 指向 時,後續對該區域的 find 呼叫會自動「跳過」所有已使用的連續區間,瞬間抵達下一個真正的可用空位。這是區間併查集非常高級且優雅的應用。
複雜度分析
- 時間複雜度:,其中 是反阿克曼函數,在實際應用中可視為常數。由於併查集帶有路徑壓縮,每次的查詢與合併操作接近 ,因此整體時間複雜度為 。
- 空間複雜度:。需要一個大小為 的併查集陣列以及雜湊表記錄狀態,空間複雜度為 。
Code
方法一:有序容器上二分
1 | class Solution: |
方法二:區間併查集
1 | class Solution: |







![Luogu 🟣 P3195 [HNOI2008] 玩具装箱](https://i.gdst.dev/cover/P3195.webp)