国庆偷懒了好几天,算法这种东西不练就废,还是赶紧捡起来,简单点就一天两三个,题目困难一点就一天一个,积少成多
很简单的贪心,就三种情况,直接根据常识就能知道找20元时,优先找一张十块和一张五块,因为两张五块能做一张十块,用户更多,直接记录五元十元的数量,遍历就出来了
class Solution {
public boolean lemonadeChange(int[] bills) {
int[] change=new int[2];//只记录5和10的钱,因为20找不了
for(int i:bills){
if(i==5) change[0]++;
if(i==10){//失去一张5元得到一张10元
change[0]--;
change[1]++;
}
if(i==20){//优先找十元加一张五元,因为两张五元的可以做一张十元的,更重要
if(change[1]>0){
change[1]--;
change[0]--;
}
else{
change[0]-=3;
}
}
if(change[0]<0||change[1]<0)//透支了
return false;
}
return true;
}
}
我为什么突然写柠檬水这个问题,就是因为写这个题目直接想当然的贪心去了,想当然的最少硬币数就是从后面开始拿,还能拿就拿,不能拿就拿下一个,明显想法是错误的,因为这个问题不止是拿最少的硬币数,更要刚好凑成这个数,比如10元在[5,8]里面拿,拿了8元就不能凑了,所以这个问题明显不能贪心,而是要动态规划,是一个完全背包问题
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp=new int[amount+1];//dp[j]记录着前i个硬币凑成j元所需要的的硬币数
dp[0]=0;//凑成0元需要0个硬币
Arrays.fill(dp,1,dp.length,Integer.MAX_VALUE);//将初始值设为最大值便于维护最小值
for(int i=0;i<coins.length;i++){//从第一个硬币开始
for(int j=coins[i];j<=amount;j++){
if(dp[j-coins[i]]!=Integer.MAX_VALUE)//只有前面的值存在才有更新的必要,不加这个会越界出现错误
dp[j]=Math.min(dp[j-coins[i]]+1,dp[j]);//维护最小值
}
}
return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];//还是最大值就是凑不成
}
}
用一个例子来看更容易理解
一开始只有一个硬币组成,然后到第二个硬币后,可以找到两个硬币组合起来的最小值,因为只有第一个硬币最小值已经确定,就是如果dp[j-coins[i]](不取当前硬币的最小值)存在,那么就dp[j]=Math.min(dp[j-coins[i]]+1,dp[j]);
取第i个硬币或者不取,看看谁更小,以此类推
太久没写这种题了,这个好像是与用滚动数组实现背包问题的方法类似。