Leetcode416:分割等和子集&&Leetcode518:零钱兑换II(动态规划之背包问题)

这篇博客介绍了如何利用动态规划解决0-1背包问题、分割等和子集以及零钱兑换II的问题。通过示例代码详细解释了动态规划的状态转移方程,并展示了如何应用这些方程来找到最优解。对于每个问题,博客都提供了清晰的思路解析,帮助读者理解动态规划在实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0-1背包问题  前提知识

框架:

int dp[][] = new int[n+1][m+1];
int dp[0][...] = 0;
int dp[...][0] = 0;
for i in [1...n]
    for j in [1...m]
    //取两种选择的最优解
    dp[i][w]= max(
        将重量a价值b的物品装进背包,
        不将重量a价值b的物品装进背包)
    return [n][w];
​

代码如下:


int knapsack(int W,int N,int[] wt,int[] val){
    //定义dp数组,初始化 dp[2][3] = 3 表示可以承重为3的前2个东西选择性放进背包的最优解(最大价值)为3
    int[][] dp = new int[N+1][W+1];
    for(int i=1;i<N;i++){
        for(int w=1;w<=W;w++){
            if(w-wt[i-1]<0){
                dp[i][w] = dp[i-1][w];
            }else{
                dp[i][w] = Math.max(
                dp[i-1][w-wt[i-1]]+val[i-1],
                dp[i-1][w]
            );
            }
            
            )
        }
    }
    return dp[N][W];
}

Leetcode416:分割等和子集

  • 题目:

    • 给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

  • 思路:

    • 将分割等和问题可以看成类似背包问题

    • 如果数组的和为奇数,则不可能将其分割成等和子集

    • 如果数组的和为偶数,则求数组的和的一半,将其一半看成背包问题,定义一个二维数组[i] [j]代表的含义,前i个数是否可以刚好恰好分成数组的和j的一半,则剩下的数自然成为另外的一半

  • 代码如下:

class Solution {
   public boolean canPartition(int[] nums) {
       int n = nums.length;
       int sum = 0;
       for(int i=0;i<n;i++){
           sum+=nums[i];
      }
       if(sum%2!=0){
           return false;
      }
       sum = sum/2;
       boolean[][] dp = new boolean[n+1][sum+1];
       for(int i = 0;i<= n;i++){
           dp[i][0] = true;
      }
​
       for(int i =1;i<=n;i++){
           for(int j =1;j<=sum;j++){
               if(j-nums[i-1]<0){
                   dp[i][j] = dp[i-1][j];
              }else{
                   dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
              }
          }
      }
       return dp[n][sum];
  }
}

Leetcode518:零钱兑换II

  • 题目:

    • 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

    请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

    假设每一种面额的硬币有无限个。

    题目数据保证结果符合 32 位带符号整数。

  • 思路:

    • 背包问题:定义二维数组dp;dp[i] [j] 代表 记录前i种硬币可以组合成j金额的组合数;

    • 没加入一种新的硬币,有如下两种情况

      • 1.所收集的总金额小于硬币金额,则选择不把新的硬币加入,返回使用i-1种硬币组合成j金额的组合数

      • 2.所收集的总金额大于硬币金额,则选择把新的硬币加入,则返回使用i-1种硬币组合成j金额的组合数 与 使用i种硬币组合成(j金额-硬币金额)的组合数之和

  • 代码如下:

class Solution {
   public int change(int amount, int[] coins) {
       int n = coins.length;
       int[][] dp = new int[n+1][amount+1];
       for(int i = 0;i<= n;i++){
           dp[i][0] = 1;
      }
       for(int i =1;i<=n;i++){
           for(int j =1;j<=amount;j++){
               if(j-coins[i-1]<0){
                   dp[i][j] = dp[i-1][j];
              }else{
                   dp[i][j] = dp[i-1][j]+dp[i][j-coins[i-1]];
              }
          }
      }
       return dp[n][amount];
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值