硬币问题-动态规划详解

基本动态规划之硬币问题

问题描述

假设有 1 元,3 元,5 元的硬币若干(无限),现在需要凑出 11 元,问如何组合才能使硬币的数量最少?

问题分析

乍看之下,我们简单的运用一下心算就能解出需要 2 个 5 元和 1 个 1 元的解。当然这里只是列出了这个问题比较简单的情况。当硬币的币制或者种类变化,并且需要凑出的总价值变大时,就很难靠简单的计算得出结论了。贪心算法可以在一定的程度上得出较优解,但不是每次都能得出最优解。

这里运用动态规划的思路解决该问题。按照一般思路,我们先从最基本的情况来一步一步地推导。

我们先假设一个函数 d(i) 来表示需要凑出 i 的总价值需要的最少硬币数量。

  1. i = 0 时,很显然我们可以知道 d(0) = 0。因为不要凑钱了嘛,当然也不需要任何硬币了。注意这是很重要的一步,其后所有的结果都从这一步延伸开来
  2. i = 1 时,因为我们有 1 元的硬币,所以直接在第 1 步的基础上,加上 1 个 1 元硬币,得出 d(1) = 1
  3. i = 2 时,因为我们并没有 2 元的硬币,所以只能拿 1 元的硬币来凑。在第 2 步的基础上,加上 1 个 1 元硬币,得出 d(2) = 2
  4. i = 3 时,我们可以在第 3 步的基础上加上 1 个 1 元硬币,得到 3 这个结果。但其实我们有 3 元硬币,所以这一步的最优结果不是建立在第 3 步的结果上得来的,而是应该建立在第 1 步上,加上 1 个 3 元硬币,得到 d(3) = 1
  5. ...

接着就不再举例了,我们来分析一下。可以看出,除了第 1 步这个看似基本的公理外,其他往后的结果都是建立在它之前得到的某一步的最优解上,加上 1 个硬币得到。得出:

d(i) = d(j) + 1

这里 j < i。通俗地讲,我们需要凑出 i 元,就在凑出 j 的结果上再加上某一个硬币就行了。

那这里我们加上的是哪个硬币呢。嗯,其实很简单,把每个硬币试一下就行了:

  1. 假设最后加上的是 1 元硬币,那 d(i) = d(j) + 1 = d(i - 1) + 1
  2. 假设最后加上的是 3 元硬币,那 d(i) = d(j) + 1 = d(i - 3) + 1
  3. 假设最后加上的是 5 元硬币,那 d(i) = d(j) + 1 = d(i - 5) + 1

我们分别计算出 d(i - 1) + 1d(i - 3) + 1d(i - 5) + 1 的值,取其中的最小值,即为最优解,也就是 d(i)

最后公式:

代码示例

这里用 Java 实现了基本的代码:

public class CoinProblemBasicTest {

    private int[] d; // 储存结果
    private int[] coins = {1, 3, 5}; // 硬币种类

    privatevoidd_func(int i, int num) {
        if (i == 0) {
            d[i] = 0;
            d_func(i + 1, num);
        }
        else {
            int min = 9999999; // 初始化一个很大的数值。当最后如果得出的结果是这个数时,说明凑不出来。
            for (int coin : coins) {
                if (i >= coin && d[i - coin] + 1 < min) {
                    min = d[i - coin] + 1;
                }
            }
            d[i] = min;

            if (i < num) {
                d_func(i + 1, num);
            }
        }
    }

    @Test
    publicvoidtest() throws Exception {
        int sum = 11; // 需要凑 11 元
        d = new int[sum + 1]; // 初始化数组

        d_func(0, sum); // 计算需要凑出 0 ~ sum 元需要的硬币数量
        for (int i = 0; i <= sum; i++) {
            System.out.println("凑齐 " + i + " 元需要 " + d[i] + " 个硬币");
        }
    }
}

结果如下:

凑齐 0 元需要 0 个硬币
凑齐 1 元需要 1 个硬币
凑齐 2 元需要 2 个硬币
凑齐 3 元需要 1 个硬币
凑齐 4 元需要 2 个硬币
凑齐 5 元需要 1 个硬币
凑齐 6 元需要 2 个硬币
凑齐 7 元需要 3 个硬币
凑齐 8 元需要 2 个硬币
凑齐 9 元需要 3 个硬币
凑齐 10 元需要 2 个硬币
凑齐 11 元需要 3 个硬币
时间: 2024-10-28 04:12:49

硬币问题-动态规划详解的相关文章

HDOJ To The Max 1081【动态规划-详解求最大子矩阵】

To The Max Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9879    Accepted Submission(s): 4762 Problem Description Given a two-dimensional array of positive and negative integers, a sub-rectan

c++的下降路径最短和----&gt;动态规划详解

#include <iostream> #include <vector> #include <algorithm> using namespace std; /*** * 分析:<----最优化问题----->此题用动态规划去写 * 1.第一步确定状态---->直接用它自己本身去写A[n][c] * 表示从第n层的第c个元素往下的最下的距离 * 2.第二步确定状态转移方程 * 状态转移方程可以分为三个部分 * 1.当c==0的时候 * A[n][c]

HDU 1024 Max Sum Plus Plus【动态规划求最大M子段和详解 】

Max Sum Plus Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 29942    Accepted Submission(s): 10516 Problem Description Now I think you have got an AC in Ignatius.L's "Max Sum" problem

动态规划之最长递增子序列问题详解

最近重新开始看动态规划,动态规划算法的有效性依赖于问题本身所具有的两个重要性质:最优子结构性质和子问题重叠性质. 1.最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质. 2.重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次.动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解. (二).动态规划算法的基本步骤设计一个标准的动态规划算法,通常

(转)dp动态规划分类详解

dp动态规划分类详解 转自:http://blog.csdn.NET/cc_again/article/details/25866971 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间效率高,代码量少,多元性强,主要考察思维能力.建模抽象能力.灵活度. ****************************************************************************************** 动态规划(英语:Dynamic programm

动态规划01背包问题(例子详解)

附上原文地址:http://www.cnblogs.com/sdjl/articles/1274312.html ----第一节----初识动态规划-------- 经典的01背包问题是这样的: 有一个包和n个物品,包的容量为m,每个物品都有各自的体积和价值,问当从这n个物品中选择多个物品放在包里而物品体积总数不超过包的容量m时,能够得到的最大价值是多少?[对于每个物品不可以取多次,最多只能取一次,之所以叫做01背包,0表示不取,1表示取] 为了用一种生动又更形象的方式来讲解此题,我把此题用另一

57. 数对之差的最大值:4种方法详解与总结[maximum difference of array]

[本文链接] http://www.cnblogs.com/hellogiser/p/maximum-difference-of-array.html [题目] 在数组中,数字减去它右边的数字得到一个数对之差.求所有数对之差的最大值.例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果. [分析] 看到这个题目,很多人的第一反应是找到这个数组的最大值和最小值,然后觉得最大值减去最小值就是最终的结果.这种思路忽略了题目中很重要的一点:数对之差

手电筒过桥问题详解

这是个很经典的问题,但是我看了很多讲解都感觉不是很系统,但是受到了看的讲解的启发,在这里希望写一个比较严谨系统的讲解. 题目是这样的:在漆黑的夜里,四位旅行者来到了一座狭窄而且没有护栏的桥边.如果不借助手电筒的话,大家是无论如何也不敢过桥去的.不幸的是,四个人一共只带了一只手电筒,而桥窄得只够让两个人同时通过.如果各自单独过桥的话,四人所需要的时间分别是1,2,5,10分钟:而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间.问题是,你如何设计一个方案,让用的时间最少.(

java文本相似度计算(Levenshtein Distance算法(中文翻译:编辑距离算法))----代码和详解

算法代码实现: package com.util; public class SimFeatureUtil { private static int min(int one, int two, int three) { int min = one; if (two < min) { min = two; } if (three < min) { min = three; } return min; } public static int ld(String str1, String str2)