动态规划实战--硬币找零问题

上一篇文章上提到硬币找零的例子,现在我们实战动态规划就从硬币找零开始

问题描述:

给定 n 种不同面值的硬币,分别记为 c[0], c[1], c[2], … c[n],同时还有一个总金额 k,编写一个函数计算出最少需要几枚硬币凑出这个金额 k?每种硬币的个数不限,且如果没有任何一种硬币组合能组成总金额时,返回 -1。

这里我们先回忆一下动态规划问题的处理过程:

我们处理动态规划问题的时候需要分为这么几步:

1)确定初始化状态,初始化状态作为整个求解链路的原点,需要优先明确;

2)状态参数,中间状态在一步一步推导出最终状态的过程中会发生变化的变量;

3)明确决策方式,即:如何通过前面的状态推导出后面的状态;

4)中间状态存储,子问题存在大量重复计算的情况,我们将中间状态存储入“备忘录”。

确定初始化状态和状态参数

原文中明确指定出了硬币的面值(多个固定值),没有限制硬币的总个数(这是我们需要求解的结果),因而这两个无法作为我们的状态参数,那么状态参数只剩下的总金额k了。

显而易见,我们可以得出结论初始化状态是总金额为0的状态

决策方式

原文要求的是使用硬币凑出金额k,那么在我们每挑出一个硬币的时候就会改变状态,这就是这道题目的决策方式

中间状态的存储

当我们需要求解k=11的时候,k=11-c[n] 会被计算,以此类推,因而我们需要有一个中间状态的决策表存储子问题的解.

状态转移方程

现在我们可以尝试得出状态转移方程

		 -1 (k<0) 异常情况也考虑下

f(k)= 0 (k=0)
min(1+f(k-c[i])) 0<i<n

到这里,感觉我们几乎可以直接写出代码了,别急,如果只是盲目的根据状态转移方程编写程序,那程序中依然存在着许多重复计算的子问题

可能从第一眼我们看这就是一个递归逻辑,子问题的答案是在不断向上返回,为什么还存在子问题的重复计算,其实我们可以展开来思考下这个程序,画个图方便理解重复计算出现在哪里

假定硬币面值是1,3,5 ,目标总金额是11

在这里插入图片描述

如图所示:由于递归是自顶向下的一个求解过程,F(7),F(5)等等在不同的分支中都会被重复计算,有一个解决方法就是将子问题缓存到中间表中,这样我们每遇到一个子问题,就到中间表中去尝试获取已知解,代码如下所示:


    public int collect(int[] coins, int target) {
   
   
        Map<Integer, Integer> cache = Maps.newHashMap();

        int result = recursionCollect
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值