题目地址:
https://www.lintcode.com/problem/lemonade-change/description
有个柠檬水摊,每瓶水价格是 5 5 5元。有一列顾客来买水,每次他们会支付 5 , 10 5,10 5,10或 20 20 20元。摊贩一开始没有钱,但每次必须给顾客正确找零。给定顾客手里的钱的序列,问该序列是否可以实现正确找零。
用两个变量存摊贩手里有多少 5 5 5和 10 10 10元( 20 20 20元钞票数量不用存,因为找零用不上)。如果顾客手里是 5 5 5元,那直接计数加 1 1 1;如果顾客手里是 10 10 10元,那此时摊贩手里必须有 5 5 5元找零,如果没有则返回false,否则将 5 5 5的计数减 1 1 1,并将 10 10 10的计数加 1 1 1;如果顾客手里是 20 20 20元,则先尽量找 10 + 5 10+5 10+5,如果找不了,则找 5 + 5 + 5 5+5+5 5+5+5,如果还找不了,则返回false。最后如果能安全遍历完序列,则返回true。
算法正确性证明:
这里用到贪心的思想,在于顾客手里
20
20
20元的时候,要优先找零
10
+
5
10+5
10+5而不是
5
+
5
+
5
5+5+5
5+5+5。首先,如果返回true,那一定存在合法找钱方案,这一点很显然。如果存在合法找钱方案,找到第一次与贪心法不同的找钱的时刻,说明这种方案找的钱是
5
+
5
+
5
5+5+5
5+5+5而不是贪心法的
10
+
5
10+5
10+5,此时,我们将这里的
5
+
5
5+5
5+5替换为
10
10
10,后面也是这样替换,如果哪里必须要用到
10
10
10了,那么就用
5
+
5
5+5
5+5顶上去。这样的方案显然也合法,它就被调整成了贪心方案,而贪心算法是返回true的,所以算法正确。
代码如下:
import java.util.List;
public class Solution {
/**
* @param bills: the Bill
* @return: Return true if and only if you can provide every customer with correct change.
*/
public boolean lemonadeChange(List<Integer> bills) {
// Write your code here.
int five = 0, ten = 0;
for (int i = 0; i < bills.size(); i++) {
int cur = bills.get(i);
if (cur == 5) {
five++;
} else if (cur == 10) {
five--;
if (five < 0) {
return false;
}
ten++;
} else {
if (five > 0 && ten > 0) {
five--;
ten--;
} else if (five >= 3) {
five -= 3;
} else {
return false;
}
}
}
return true;
}
}
时间复杂度 O ( n ) O(n) O(n), n n n为顾客数量,空间 O ( 1 ) O(1) O(1)。