问题描述
问题:在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
这道题是leetcode里面的题目:
解题思路
考虑用贪心策略来求解问题:
找零会遇到三种情况,分别是对5美元找零、对10美元找零、对20美元找零。
- 五美元:无需要找零,五美元+1
- 十美元:找五美元,十美元+1
- 二十美元:找三张五美元或者找一张十美元一张五美元,二十美元+1
可以看到我们的找零策略只有一种分支情况,可以发现我们五美元在找零十美元和二十美元时都是需要用到的,因此我们可以认为5美元更有价值。或者从另一个角度来看,十美元只有在二十美元找零的情况下才需要用到,因此10美元可以考虑优先找出。
那么我们的贪心策略就是,找零二十美元时,优先考虑10+5的组合。
算法实现
class Solution {
public:
bool lemonadeChange(vector<int>& bills)
{
vector<int>moneny(2);
for (auto& e : bills)
{
if (e == 5)
moneny[0]++;
else if (e == 10)
{
moneny[0]--;
moneny[1]++;
}
else
{
moneny[0]--;
if (moneny[1])moneny[1]--;
else moneny[0] -= 2;
}
if (moneny[0] < 0)return false;
}
return true;
}
};
正确性证明
我们这里采取交换论证法,如果可以将最优策略转化为贪心策略,那就证明了这个算法的正确性。
最优策略:
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an
贪心策略:
b
1
,
b
2
,
.
.
.
,
b
n
b_1,b_2,...,b_n
b1,b2,...,bn
这里的项表示找零的方法,又因为最优策略和贪心策略对于5美元、10美元的找零策略相同,故只需要考虑对20美元的找零策略。
又因为最优策略中对于20美元的找零方案只有10+5和5+5+5,考虑am是首个5+5+5的策略。这时对应的贪心策略为10+5.
也就是最优策略省下了10美元没用,现在分两种情况:
第一种,最优策略在后续的找零策略中依然没使用这10美元。很显然我们可以将am置换为5+5+5,结果依然正确,即我们将最优策略转化为了贪心策略。
第二种,an,n>m使用了10+5方案,那我们可以交互am和an的位置。然后再对an讨论两种情况。
综上,我们将最优策略转换成了贪心策略。