由Coin change引发的动态规划热情

一、先来看看Coin change

输入是不同的面额和一个总金额,写一个函数计算组成这个总金额所需的最少的硬币数目。如果无法组成该金额,返回-1。

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

思路:假设dp[i]是组成i的最少硬币数,考虑去掉一个面额x的硬币的情况,dp[i-x]是否是组成i-x的最少硬币数?

用反证法,如果dp[i-x]不是组成i-x的最少硬币数,那么就可以用少于dp[i-x]的硬币数组成i-x,相应的i就可以用这个更少的组合+一块x面额的硬币组成,那么大前提里的dp[i]就不是最少硬币数,矛盾。

结论,如果dp[i]是组成i的最少硬币数,那么去掉一个面额为x的硬币后,dp[i-x]也是组成i-x的最少硬币数。

动态规划的转移方程就出来了:dp[i] = min{ dp[i], dp[i-coins[j]] + 1 }

代码:

public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        
        Arrays.fill(dp,amount+1);    //组成amount的最小硬币数 最少是amount个,也就是全1的情况
        dp[0] = 0;


        for(int i=1; i<=amount; i++)
            for(int j=0; j<coins.length; j++)
                if(i>=coins[j])
                    dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);


        return dp[amount]==amount+1?-1:dp[amount];
}

二,求一种具体方案

 

前面只是算出了最少需要多少硬币,但是没有给出具体方案。下面考虑一下如何给出具体方案,

其实可以根据dp[amount]是如何推出来了,去反推出方案。打个比方,现在有coins = [2,3,5],dp[12]求出来是3

那么dp[12]一定是根据dp[10],dp[9],dp[7]中的最小值dp[12-x]退出来的,且有dp[12] = dp[12-x]+1,也就意味着这一步用了一个面值是x的硬币。这样一直朝前推就可以把方案算出来。

 

private static int[] fangan(int amount, int[] coins, int[] dp) {
	int[] count = new int[coins.length];
	int t = amount;
	while (t > 0)
		for (int i = 0; i < coins.length; i++)
			if (t >= coins[i]) 
				if (dp[t] == dp[t - coins[i]] + 1) {
					count[i] += 1;
					t -= coins[i];
					break;
				}

	return count;
}

这里只能求出一种方案,并不能求出所有方案,比如如果amount=9的话,那么count = [2, 0, 1]。但实际上,我们知道,3个3的方案也是可行的。

这里可以试着把break;去掉,也是可以找到一个方案的,虽然没有加上break;好理解,但是其实逻辑是一样的。

三,求所有方案

 

还是上面的例子,amount=9可能有多个多种方案,那么amount-conis[i]最多有三种方案,然后再去递归amount=7,6,4的方案即可。具体怎么写,待我神功大成之日再来写。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值