zoukankan      html  css  js  c++  java
  • 分硬币问题

    分硬币问题是一个很有意思的问题,一般可以用recursive或者dp来解决。Reference里面acm之家的文章分析得很好,下面我自己再重复一边这个过程。这道题也可以联想到其他dp过程。

    题目很简单: 给定一堆硬币coins[], 数量管够,硬币有m种不同的面值,求组成 n 元钱有多少种不同的方法。

    1. 首先我们考虑递归。
      1. 当n < 0的时候,我们返回0
      2. 当n = 0的时候,我们返回1,只有一种组合就是所有硬币都取0个
      3. 当m <= 0的时候,没有可以用的硬币种类了,我们返回0
      4. 其他情况,我们可以分为两种
        1. 不使用第m种硬币,那么我们可以递归求解子问题,结果应该是count(coins, m - 1, n)
        2. 至少使用一次第m种硬币,也是分解为相同子问题,结果是count(coins, m, n - coins[m - 1])
    2. 接下来我们考虑用dynamic programming来解决,也就是把上面的逻辑转换为记忆化搜索
      1. 首先我们要建立一个数组dp[][] = new int[n + 1][m]
      2. 初始化这个数组,把第一行设置为1。因为这里n = 0,所以我们每个硬币都只有一种方法,就是不选择这些硬币,所以第一行都为1
      3. 当i 从 1 到  dp.length, j从0到m进行遍历的时候,我们依然是分为两种情况进行考虑
        1. 使用第j种硬币:
          1. 那么假如当前  i - coins[j] >= 0的话,我们可以使用第j种硬币,相应的值为dp[i - coins[j]][j]。意思是我们减掉这个coins[j]的值,到之前已经保存的i - coins[j]这一行的第j列去看有多少种方法
          2. 否则我们不能使用这类硬币,值为0
        2. 不适用第j种硬币:
          1. 那么假如j > 1的话,我们可以有多少种方法完全决定于同一行内的上一个数据dp[i][j - 1]
          2. 否则值为0
        3. 把这两种情况综合一下 x + y, 就是dp[i][j]应该有的值了。
    3. 接下来我们对上面的dp进行空间上的简化,用滚动数组来代替二维数组:
      1. 声明一个滚动数组table = new int[n + 1]
      2. table[0] = 1
      3. 当i 从 0 到 m, j 从dp[i] 到 n + 1遍历的时候 table[j] += table[j - coins[i]]
      4. 举个例子,假如 n = 10,我们有1, 2 ,5这三种硬币,那么过程如下
        1. 初始化                [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        2. 使用1                     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        3. 使用1和2                [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]
        4. 使用1,2和5             [1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 10]

    C#:

    2D DP

                int[] coins = { 1, 2, 5 };
                int m = coins.Length;
                int n = 100;
                int[,] dp = new int[n + 1, m];
                for (int i = 0; i < m; i++)
                {
                    dp[0, i] = 1;
                }
    
                for (int i = 1; i < n + 1; i++)         // 2d dp
                {
                    for (int j = 0; j < m; j++)
                    {
                        int x = (i - coins[j] >= 0) ? dp[i - coins[j], j] : 0;
                        int y = (j > 0) ? dp[i, j - 1] : 0;
                        dp[i, j] = x + y;
                    }
                }
                Console.WriteLine(dp[n, m - 1]);

    滚动数组DP

                int[] coins = { 1, 2, 5 };
                int m = coins.Length;
                int n = 100;
             
                int[] table = new int[n + 1];
                table[0] = 1;
                for (int i = 0; i < m; i++)
                {
                    for (int j = coins[i]; j < table.Length; j++)
                    {
                        table[j] += table[j - coins[i]];
                    }
                }
                Console.WriteLine(table[n]);

    Update:  6-20-2016

    To be updated:  

    1. n元钱有多少种组成方式

    2. 最少可以用多少个硬币来组成n元钱

    Reference:

    http://www.acmerblog.com/dp6-coin-change-4973.html

    http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/

  • 相关阅读:
    芯片测试
    【转】悬浮的对话框
    imagebutton 设置点击和按压效果
    imagebutton 设置了src属性的图片更换
    侧滑实现
    使用自定义的AlertDialog。
    [转]Dialog
    【转】webview的几个问题
    webview 播放H5视频问题 黑屏 只有声音没有画面
    【转】Android HTML5 Video视频标签自动播放与自动全屏问题解决
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4941500.html
Copyright © 2011-2022 走看看