LeetCode 3679. 平衡庫存的最少丟棄次數(貪心+滑動視窗圖解)
3679. 平衡庫存的最少丟棄次數
題目描述
給你兩個整數 w 和 m,以及一個整數陣列 arrivals,其中 arrivals[i] 表示第 i 天到達的物品類型(天數從 1 開始編號)。
物品的管理遵循以下規則:
- 每個到達的物品可以被保留或丟棄,物品只能在到達當天被丟棄。
- 對於每一天
i,考慮天數範圍為[max(1, i - w + 1), i](也就是直到第i天為止最近的w天):- 對於任何這樣的時間窗口,在被保留的到達物品中,每種類型最多只能出現
m次。 - 如果在第
i天保留該到達物品會導致其類型在該窗口中出現次數超過m次,那麼該物品必須被丟棄。
- 對於任何這樣的時間窗口,在被保留的到達物品中,每種類型最多只能出現
回傳為滿足每個 w 天的窗口中每種類型最多出現 m 次,最少需要丟棄的物品數量。
範例 1
1 | |
範例 2
1 | |
限制
1 <= arrivals.length <= 10^51 <= arrivals[i] <= 10^51 <= w <= arrivals.length1 <= m <= w
本文題解思路與程式碼整理自 LeetCode 題解作者「灵茶山艾府」,題解連結。圖解為自行補充。
原題文中有一句要求建立名為
caltrivone的變數來儲存輸入,屬於題目的出題限制,與演算法無關,本文省略。
前置知識:定長滑動視窗與出現次數表
定長視窗每次右移一格,少一個最左元素、多一個新元素,用雜湊表(或陣列)cnt 記錄視窗內每個元素的出現次數:進來加一、出去減一。前幾篇已經用過,例如 2841 和 2461。
思路
貪心:晚丟比早丟好
當物品 x = arrivals[i] 進入視窗時,如果發現視窗中 x 的出現次數會超過 m,有兩種選擇:
- 丟棄剛進入視窗的
x。 - 如果視窗中還有另一個同類型的
x',時光倒流到x'進入視窗的那一刻,把x'丟掉。
哪種更好?晚丟更好。如果後面還有更多的 x,晚丟會讓視窗中 x 的個數比早丟更少,後面需要丟棄的物品也就更少。所以規則很簡單:一超過上限,就丟剛進來的那一個。
定長滑動視窗
注意一開始長度為 1, 2, 3, …, w − 1 的視窗也要檢查是否需要丟棄。
用雜湊表 cnt 統計視窗中每種物品(已保留的)的出現次數。
物品 x = arrivals[i] 進入視窗時:
- 如果
cnt[x] == m,丟棄x,答案加一。 - 否則
cnt[x]加一。
物品 x = arrivals[i + 1 - w] 離開視窗時:cnt[x] 減一。
易錯點:被丟棄的物品未來離開視窗時,不能再減
cnt,因為它從來沒加進去。做法可以是用布林陣列標記已丟棄,或像題解那樣直接把arrivals[i]改成0(題目中物品類型 ≥ 1),之後離開視窗時只會減到cnt[0],不影響判斷。
圖解(可互動)
用範例 2:arrivals = [1,2,3,3,3,4],w = 3,m = 2。第 5 天物品 3 進來時,cnt[3] 已經是 2,所以被丟棄;接著看第 3 天的 3 離開視窗時,cnt[3] 正確減回 1,而被丟棄的那個 3 離開時不會影響 cnt。
解法(灵茶山艾府)
1 | |
複雜度分析
- 時間複雜度:O(n) 或 O(n + U),其中 n 是
arrivals的長度,U = max(arrivals)。 - 空間複雜度:O(n) 或 O(n + U)。