当前位置: 首页 > news >正文

零钱兑换(coin-change) 动态规划问题

文章目录

  • 零钱兑换(coin-change)
    • 思路
    • java代码
    • C++代码
    • 说明
    • 参考资料

零钱兑换(coin-change)

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1

示例 2:

输入: coins = [2], amount = 3
输出: -1

说明:
你可以认为每种硬币的数量是无限的。

思路

假设你是个土豪,你有1,5,10,20,50,100的钞票,你要凑出666买瓶水喝,依据生活经验,我们一般采取这样的策略:能用100就用100的,否则就用50的,依此类推,在这种策略下,666=100*6 + 50 1 + 10 1 + 51 + 11, 一共用了10张钞票。

这种策略就称为 贪心策略 :贪心策略是在当前情况下做出最好的选择,根据需要凑出的金额来进行贪心,但是,如果我们换一组钞票面值,比如 1, 5, 11,我们要凑出15的时候, 贪心策略就会出错:

15 = 11 * 1 + 1 * 4 (贪心策略)
15 = 5 * 3(正确策略)
贪心策略哪里出错了?
鼠目寸光

重新分析刚刚的例子。w=15时,我们如果取11,接下来就面对w=4的情况;如果取5,则接下来面对w=10的情况。我们发现这些问题都有相同的形式:“给定w,凑出w所用的最少钞票是多少张?” 接下来,我们用f(n)来表示“凑出n所需的最少钞票数量”。  
那么,如果我们取了11,最后的代价(用掉的钞票总数)是多少呢?
  
明显 ,它的意义是:利用11来凑出15,付出的代价等于f(4)加上自己这一张钞票。现在我们暂时不管f(4)怎么求出来。
依次类推,马上可以知道:如果我们用5来凑出15,cost就是f(10) + 1 = 2 + 1 = 3 。 
 那么,现在w=15的时候,我们该取那种钞票呢?当然是各种方案中,cost值最低的那一个

  • 取11: cost=f(4)+1=4+1=5
  • 取5:   cost = f(10) + 1 = 2 + 1 = 3
  • 取1:  cost = f(14) + 1 = 4 + 1 = 5
    显而易见,cost值最低的是取5的方案。我们通过上面三个式子,做出了正确的决策!
    这给了我们一个至关重要的启示—— 只与 相关;更确切地说: f(n) 只与 f(n-1),f(n-5),f(n-11) 相关;更确切地说:
    f(n)=min{f(n-1),f(n-5),f(n-11)}+1

java代码

package coin_change;

public class Solution {
	public int coinChange(int[] coins, int amount) {
		int[] dp = new int[amount + 1];
		for (int i = 0; i <= amount; i++) {
			dp[i] = -1;
		}
		dp[0] = 0;
		// 外层循环金额,内层循环面值
		for (int i = 1; i <= amount; i++) {// 循环各个金额,找到dp[i]最优解
			for (int j = 0; j < coins.length; j++) {// 递推条件
				if (i - coins[j] >= 0 && dp[i - coins[j]] != -1) {
					if (dp[i] == -1 || dp[i] > dp[i - coins[j]] + 1) {
						dp[i] = dp[i - coins[j]] + 1;
					}
				}
			}
		}
		return dp[amount];
	}

	public static void main(String[] args) {
		Solution solu = new Solution();
		int[] coins = { 1, 2, 5, 7, 10 };
		int amount = 14;
		for (int i = 0; i <= amount; i++) {
			System.out.printf("dp[%d] = %d\n", i, solu.coinChange(coins, i));
		}
	}
}

输出

dp[0] = 0
dp[1] = 1
dp[2] = 1
dp[3] = 2
dp[4] = 2
dp[5] = 1
dp[6] = 2
dp[7] = 1
dp[8] = 2
dp[9] = 2
dp[10] = 1
dp[11] = 2
dp[12] = 2
dp[13] = 3
dp[14] = 2

C++代码

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        std::vector<int> dp;
        for(int i=0;i<=amount;i++){
        	dp.push_back(-1);
        }

        dp[0] = 0;//金额0最优解0
        //外层循环金额,内层循环面值
        for(int i = 1;i<=amount;i++){//循环各个金额,找到dp[i]最优解
            for(int j = 0;j<coins.size();j++){//递推条件
                if(i-coins[j]>=0 && dp[i-coins[j]] != -1){
                    if(dp[i] == -1 || dp[i] > dp[i-coins[j]]+1){
                    	dp[i] = dp[i-coins[j]]+1;
                    }
                }
            }
        }
        return dp[amount];
    }
};

说明

这里设置多了一个面值0.
dp[0] = 0
dp[1] = 1 + dp[0]
dp[2] = 1 + dp[0]
dp[5] = 1 + dp[0]
dp[7] = 1 + dp[0]
dp[10] = 1 + dp[0]

coins = {1,2,5,7,10}
i代表金额,coins[j]代表第j个面值的金额:
i-coins[j] >=0dp[i-coins[j]] != -1 时:
j = 0,1,2,3,4 ; coins[j] = 1,2,5,7,10
dp[i] = getmin(dp[i - coins[j]]) +1

参考资料

参考–hikes
小象Leecode第九课_动态规划

相关文章:

  • 批量识别图版中的文字信息之百度AI文字识别
  • 操作系统 术语表
  • GoldenDict 调用百度翻译(多段文本)
  • Hexo常用命令
  • 最小路径和(minimum-path-sum)
  • Leetcode.4.寻找两个有序数组的中位数(problems/median-of-two-sorted-arrays)
  • python调试PDB工具命令
  • PAT乙级介绍
  • PAT乙级1011. A+B和C(C语言)
  • 错误: 找不到或无法加载主类 org.apache.zookeeper.server.quorum.QuorumPeerMain
  • sql优化的几种方法(面试必背)
  • mysql的性能优化(经典必看)
  • 路径总和 II
  • 二叉树的所有路径(binary-tree-paths)
  • Deepin中的fcitx输出的文字变繁体
  • 【个人向】《HTTP图解》阅后小结
  • CSS 三角实现
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • JavaScript 基础知识 - 入门篇(一)
  • mockjs让前端开发独立于后端
  • php的插入排序,通过双层for循环
  • React+TypeScript入门
  • RxJS: 简单入门
  • vue-router的history模式发布配置
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 批量截取pdf文件
  • 什么软件可以剪辑音乐?
  • 思否第一天
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 正则与JS中的正则
  • ​​​​​​​​​​​​​​Γ函数
  • #QT(智能家居界面-界面切换)
  • #单片机(TB6600驱动42步进电机)
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (26)4.7 字符函数和字符串函数
  • (4)事件处理——(7)简单事件(Simple events)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (阿里云万网)-域名注册购买实名流程
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (七)Java对象在Hibernate持久化层的状态
  • ./configure,make,make install的作用
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • [1] 平面(Plane)图形的生成算法
  • [Angular] 笔记 18:Angular Router
  • [Angular] 笔记 21:@ViewChild
  • [C#] 如何调用Python脚本程序
  • [C#]winform制作仪表盘好用的表盘控件和使用方法
  • [daily][archlinux][game] 几个linux下还不错的游戏
  • [EFI]ASUS EX-B365M-V5 Gold G5400 CPU电脑 Hackintosh 黑苹果引导文件