算法日常训练10.11

国庆偷懒了好几天,算法这种东西不练就废,还是赶紧捡起来,简单点就一天两三个,题目困难一点就一天一个,积少成多

 很简单的贪心,就三种情况,直接根据常识就能知道找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个硬币或者不取,看看谁更小,以此类推

太久没写这种题了,这个好像是与用滚动数组实现背包问题的方法类似。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值