zoukankan      html  css  js  c++  java
  • LeetCode——零钱兑换 II

    Q:给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

    示例 1:
    输入: amount = 5, coins = [1, 2, 5]
    输出: 4
    解释: 有四种方式可以凑成总金额:
    5=5
    5=2+2+1
    5=2+1+1+1
    5=1+1+1+1+1
    示例 2:
    输入: amount = 3, coins = [2]
    输出: 0
    解释: 只用面额2的硬币不能凑成总金额3。
    示例 3:
    输入: amount = 10, coins = [10]
    输出: 1

    A:
    1.回溯法
    超时。
    为避免重复的情况(【1,2】和【2,1】),设定coins是递增的,且设定一个index,只能使用index后的coin进行组装。

        private int sum;
    //    private ArrayList<Integer> array = new ArrayList<>(); //查看实际情况
        public int change(int amount, int[] coins) {
            if (amount == 0)
                return 1;
            else if (coins.length == 0)
                return 0;
            sum = 0;
            Arrays.sort(coins);
            coin(amount, coins, 0);
            return sum;
        }
    
        private void coin(int amount, int[] coins, int index) {//设定index,保证递增存入
            if (amount == 0) {
                sum++;
                return;
            }
            for (int i = index; i < coins.length; i++) {
                if (amount >= coins[i]) {
                    amount -= coins[i];
    //                array.add(coins[i]);
                    coin(amount, coins, i);
    //                array.remove(array.size() - 1);
                    amount += coins[i];
                }
            }
        }
    

    2.动态规划
    完全背包问题。
    dp[i][j]的定义如下:
    若只使用前i个物品,当背包容量为j时,有dp[i][j]种方法可以装满背包。
    换句话说,翻译回我们题目的意思就是:
    若只使用coins中的前i个硬币的面值,若想凑出金额j,有dp[i][j]种凑法。

    经过以上的定义,可以得到:
    base case 为dp[0][..] = 0, dp[..][0] = 1。因为如果不使用任何硬币面值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。
    我们最终想得到的答案就是dp[N][amount],其中N为coins数组的大小。
    大致的伪码思路如下:

    int dp[N+1][amount+1]
    dp[0][..] = 0
    dp[..][0] = 1
    
    for i in [1..N]:
        for j in [1..amount]:
            把物品 i 装进背包,
            不把物品 i 装进背包
    return dp[N][amount]
    

    如果你不把这第i个物品装入背包,也就是说你不使用coins[i]这个面值的硬币,那么凑出面额j的方法数dp[i][j]应该等于dp[i-1][j],继承之前的结果。
    如果你把这第i个物品装入了背包,也就是说你使用coins[i]这个面值的硬币,那么dp[i][j]应该等于dp[i][j-coins[i-1]]。

        public int change(int amount, int[] coins) {
            if (amount == 0)
                return 1;
            else if (coins.length == 0)
                return 0;
            int[][] dp = new int[coins.length + 1][amount + 1];
            for (int i = 1; i <= coins.length; i++) {
                dp[i][0] = 1;
            }
            for (int i = 1; i <= coins.length; i++) {
                for (int j = 1; j <= amount; j++) {
                    dp[i][j] = dp[i - 1][j];//不加入这枚硬币
                    if (j - coins[i - 1] >= 0)//加入这枚硬币
                        dp[i][j] += dp[i][j - coins[i - 1]];                    
                }
            }
            return dp[coins.length][amount];
        }
    

    3.转成一维数组

    int change(int amount, int[] coins) {
        int n = coins.length;
        int[] dp = new int[amount + 1];
        dp[0] = 1; // base case
        for (int i = 0; i < n; i++)
            for (int j = 1; j <= amount; j++)
                if (j - coins[i] >= 0)
                    dp[j] = dp[j] + dp[j-coins[i]];
    
        return dp[amount];
    }
    
  • 相关阅读:
    派生类的构造函数
    继承和派生
    自增自减运算符的重载(强制类型转换运算符重载)
    流插入和流提取运算符的重载
    动态数组类的设计
    函数的返回值
    赋值运算符的重载
    运算符重载
    常量对象函数引用和参数传递
    理解ASP.NET MVC的路由系统
  • 原文地址:https://www.cnblogs.com/xym4869/p/13024462.html
Copyright © 2011-2022 走看看