代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

完全背包

完全背包
文章讲解:https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85.html
题目链接:https://kamacoder.com/problempage.php?pid=1052
视频讲解:https://www.bilibili.com/video/BV1uK411o7c9/

自己看到题目的第一想法

完全背包是指背包里的物品能随便用几次。01背包的话背包里的物品是能使用一次。

看完代码随想录之后的想法

完全背包是指背包里的物品能随便用几次。01背包的话背包里的物品是能使用一次。
完全背包和01背包的区别:

  • 遍历顺序
    • 01背包遍历时,物品从小到大遍历,背包从大到小遍历。(逆序可以把dp数组可以打印出来,看一下dp数组状态是如何转移的。)硬想想不懂,还是打印出来能理解。
    • 完全背包遍历时,物品从小到大遍历,背包也从小到大遍历。(正序也可以把dp数组可以打印出来,看一下dp数组状态是如何转移的。)
    • 完全背包遍历时,还可以先遍历背包后遍历物品。
      • 原因:先物后背从左到右,以行的方式更新。先背后物品,以列的方式更新。
      • 代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ_第1张图片

自己实现过程中遇到哪些困难

零钱兑换 II

518. 零钱兑换 II
文章讲解:https://programmercarl.com/0518.%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2II.html
题目链接:https://leetcode.cn/problems/coin-change-ii/
视频讲解:https://www.bilibili.com/video/BV1KM411k75j

自己看到题目的第一想法

列出二维组合矩阵,查看一下规律。
dp定义给出来了,但是二维递推公式没想出来。

  • dp数组定义:dp[i][j] 0-i个物品,在j的背包重量下,凑成重量j的硬币组合数
  • 确定递推公式:二维递推公式没想出来(这里直接用一下别人的图,不拍自己纸上画的了。)
    代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ_第2张图片

看完代码随想录之后的想法

没看懂,看leetcode官方题解去了:

  • 确定dp定义:用dp[x]表示金额之和等于x的硬币组合数,目标是求dp[amount]。
  • 确定递推公式:对于面额为coin的硬币,当coin <= i <= amount时,如果存在一种硬币组合的金额之和等于i - coin。则在该硬币组合中增加一个面额为coin的硬币,即可得到一种金额之和等于i的硬币组合。(所以遍历coin,然后拿到)

动态规划做法:

  • 初始化dp[0] = 1;
  • 遍历coins,对于其中每个coin进行如下操作:
    • 遍历i从coin到amount,将dp[i-coin]的值加到dp[i]。
    • 这里的递推公式推导
      • 只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。
      • 例如:dp[j],j 为5,
        • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
        • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
        • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
        • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
        • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
        • 那么凑整dp[5]有多少方法就是把 所有的 dp[j - nums[i]] 累加起来。
  • 最终得到dp[amount]的值即为答案。

自己实现过程中遇到哪些困难

递推公式的推导没理解

public int change(int amount, int[] coins) {
     int[] dp = new int[amount + 1];
     dp[0] = 1;
     for(int i = 0;i < coins.length;i++){// 遍历物品
         for(int j = coins[i]; j <= amount; j++){// 遍历背包
              dp[j] += dp[j - coins[i]];
         }
     }
     /**
     coin:1,dp[1]:1
     coin:1,dp[2]:1
     coin:1,dp[3]:1
     coin:1,dp[4]:1
     coin:1,dp[5]:1
     coin:2,dp[2]:2
     coin:2,dp[3]:2
     coin:2,dp[4]:3
     coin:2,dp[5]:3
     coin:5,dp[5]:4
      */
     return dp[amount];

 }

组合总和 Ⅳ

 377. 组合总和 Ⅳ  
文章讲解:https://programmercarl.com/0377.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C%E2%85%A3.html
题目链接:https://leetcode.cn/problems/combination-sum-iv/
视频讲解:https://www.bilibili.com/video/BV1V14y1n7B6/

自己看到题目的第一想法

这道题目取得是排列数,和上一题的组合数不一样,这里用的是排列数的话遍历的顺序不一致。

看完代码随想录之后的想法

coins[1,2,3]
target = 4
1+1+1+1
1+1+2
1+2+1
2+1+1
2+2
1+3
3+1
输出7
和零钱兑换的区别就是遍历顺序。

遍历顺序看了一遍还是看不懂

public int combinationSum4(int[] nums, int target) {
    int[] dp = new int[target + 1];
    dp[0] = 1;

    for (int i = 0; i < nums.length; i++) { // 遍历物品
        for (int j = nums[i]; j <= target; j++) { // 遍历背包容量
            dp[j] += dp[j - nums[i]];
            System.out.println("dp[" + j + "]:"+dp[j]);
        }
    }
    /**
    nums = [1,2,3]  target=4
    dp[1]:1
    dp[2]:1
    dp[3]:1
    dp[4]:1
    dp[2]:2
    dp[3]:2
    dp[4]:3
    dp[3]:3
    dp[4]:4
     */

    
    
    // for (int j = 0; j <= target; j++) { // 遍历背包容量
    //     for (int i = 0; i < nums.length; i++) { // 遍历物品
    //         if (j - nums[i] >= 0) dp[j] += dp[j - nums[i]];
    //         System.out.println("dp[" + j + "]:"+dp[j]);
    //     }
    // }
    // /**
    // dp[0]:1
    // dp[0]:1
    // dp[0]:1
    // dp[1]:1
    // dp[1]:1
    // dp[1]:1
    // dp[2]:1
    // dp[2]:2
    // dp[2]:2
    // dp[3]:2
    // dp[3]:3
    // dp[3]:4
    // dp[4]:4
    // dp[4]:6
    // dp[4]:7
    //  */



    return dp[target];
}

今日收获&学习时长

后面再回顾

你可能感兴趣的:(算法)