860.檸檬水找零
在檸檬水攤上,每一杯檸檬水的售價為 5 美元。
顧客排隊購買你的產品,(按賬單 bills 支付的順序)一次購買一杯。
每位顧客只買一杯檸檬水,然後向你付 5 美元、10 美元或 20 美元。你必須給每個顧客正確找零,也就是說淨交易是每位顧客向你支付 5 美元。
注意,一開始你手頭沒有任何零錢。
如果你能給每位顧客正確找零,返回 true ,否則返回 false 。
示例 1:輸入:[5,5,5,10,20]輸出:true解釋:前 3 位顧客那裡,我們按順序收取 3 張 5 美元的鈔票。第 4 位顧客那裡,我們收取一張 10 美元的鈔票,並返還 5 美元。第 5 位顧客那裡,我們找還一張 10 美元的鈔票和一張 5 美元的鈔票。由於所有客戶都得到了正確的找零,所以我們輸出 true。
示例 2:輸入:[5,5,10]輸出:true
示例 3:輸入:[10,10]輸出:false
示例 4:輸入:[5,5,10,10,20]輸出:false解釋:前 2 位顧客那裡,我們按順序收取 2 張 5 美元的鈔票。對於接下來的 2 位顧客,我們收取一張 10 美元的鈔票,然後返還 5 美元。對於最後一位顧客,我們無法退回 15 美元,因為我們現在只有兩張 10 美元的鈔票。由於不是每位顧客都得到了正確的找零,所以答案是 false。
提示:
0 <= bills.length <= 10000bills[i] 不是 5 就是 10 或是 20思路這是前幾天的leetcode每日一題,感覺不錯,給大家講一下。
這道題目剛一看,可能會有點懵,這要怎麼找零才能保證完整全部賬單的找零呢?
「但仔細一琢磨就會發現,可供我們做判斷的空間非常少!」
只需要維護三種金額的數量,5,10和20。
有如下三種情況:
情況一:賬單是5,直接收下。情況二:賬單是10,消耗一個5,增加一個10情況三:賬單是20,優先消耗一個10和一個5,如果不夠,再消耗三個5此時大家就發現 情況一,情況二,都是固定策略,都不用我們來做分析了,而唯一不確定的其實在情況三。
而情況三邏輯也不復雜甚至感覺純模擬就可以了,其實情況三這裡是有貪心的。
賬單是20的情況,為什麼要優先消耗一個10和一個5呢?
「因為美元10只能給賬單20找零,而美元5可以給賬單10和賬單20找零,美元5更萬能!」
所以區域性最優:遇到賬單20,優先消耗美元10,完成本次找零。全域性最優:完成全部賬單的找零。
區域性最優可以推出全域性最優,並找不出反例,那麼就試試貪心演算法!
C++程式碼如下:
class Solution {public: bool lemonadeChange(vector<int>& bills) { int five = 0, ten = 0, twenty = 0; for (int bill : bills) { // 情況一 if (bill == 5) five++; // 情況二 if (bill == 10) { if (five <= 0) return false; ten++; five--; } // 情況三 if (bill == 20) { // 優先消耗10美元,因為5美元的找零用處更大,能多留著就多留著 if (five > 0 && ten > 0) { five--; ten--; twenty++; // 其實這行程式碼可以刪了,因為記錄20已經沒有意義了,不會用20來找零 } else if (five >= 3) { five -= 3; twenty++; // 同理,這行程式碼也可以刪了 } else return false; } } return true; }};
總結咋眼一看好像很複雜,分析清楚之後,會發現邏輯其實非常固定。
這道題目可以告訴大家,遇到感覺沒有思路的題目,可以靜下心來把能遇到的情況分析一下,只要分析到具體情況了,一下子就豁然開朗了。
如果一直陷入想從整體上尋找找零方案,就會把自己陷進去,各種情況一交叉,只會越想越複雜了。
打算從頭開始打卡的錄友,可以在「演算法彙總」這裡找到歷史文章,很多錄友都在從頭打卡,你並不孤單!
我是程式設計師Carl,個人主頁:https://github.com/youngyangyang04