零钱兑换 II

119 阅读1分钟

题目

image.png

动态规划

public int change(int amount, int[] coins) {

        if (amount == 0) {
            return 0;
        }

        // dp[i][j]代表选择了前i种硬币, 能装满j的种数
        int [][] dp = new int[coins.length + 1][amount + 1];

        // base case 目标金额为0, 那么不管给多少硬币, 只有一种装法 即[]
        for (int i = 0; i <= coins.length; i ++) {
            dp[i][0] = 1;
        }

        for (int i = 1; i <= coins.length; i ++) {
            for (int j = 1; j <= amount; j ++) {
                if (j - coins[i - 1] >= 0) {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }

            }
        }
        return dp[coins.length][amount];

    }

思路

(1) 按照背包问题去理解, 因此一定是个二维dp数组, 硬币的种类是i, 目标金额就是背包空间的大小, dp[i][j] 就是选择前i种硬币, 填满j大小的背包一共有多少种可能

(2) 注意base case, 背包大小为0的时候, 装满背包的方案只有一种, 那就是不装.

(3) 既然是必须恰好装满背包, 那判断条件为啥是j - coins[i - 1] >= 0呢? 而不是j - coins[i - 1] == 0呢? 因为是这样的, 当前的硬币值是coins[i - 1], 那么就假设当前背包是空的, 能否放下这个硬币, 而不是说背包已经装的只差这枚硬币了, 因此是 >= 0, 而不是 ==0;

当j - coins[i - 1] > 0 的时候, 说明可以放下这枚硬币, 到底放下之后能不能凑满整个背包, 就是由dp[i, j - coins[i - 1]]决定的了.

(4) 为啥状态转移方程是 dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];

因为可以放, 但是不放, 是一种情况, 放了, 是另一种情况, 答案应该是这两种情况的次数相加