代码随想录Day_42打卡

01背包问题

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

动态规划:

        创建一个二维数组dp[i][j],其中i表示物品的序号,j表示当前背包的容量,dp[i][j]表示使用0-i下标的物品,背包容量为j的最大价值总和。此时,dp[i][j]其实跟上一行dp[i - 1][j]的区别就在于是否使用物品i,故dp[i][j]的值相当于dp[i - 1][j]和dp[i][j - weight[i]] + value[i]的最大值。遍历顺序可以随意,可以先遍历物品也可以先遍历背包容量,只要保证两个元素都是从小到大即可。因为每个最大价值和在数组上都是由左上区域推出来的,所以只要保证两个都是从小到大就行。

总结:

        dp定义及含义:dp[i][j]表示使用0-i下标的物品,背包容量为j的最大价值和。

        状态转移方程:dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - weight[i]] + value[i])。

        初始化:第一列都是0,因为背包容量为0,放不下东西,第一列则根据第一个物品赋值

        遍历顺序:物品和重量保证从小到大即可,两者之间的顺序无所谓。

        dp[n][w]即为所要求的值。

优化:        使用一维数组

        根据状态方程:dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - weight[i]] + value[i]),在遍历赋值的每一行中,其实就是使用了前一行的数值dp[i - 1][j],i表示物品,可以进行优化。直接省略物品,在外层循环里判断j是否大于物品重量即可。即创建一个一维数组dp[j],状态转移方程改为:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])。遍历顺序:先遍历物品,背包容量倒序遍历。因为一维数组dp不能保证物品的唯一使用性,而dp的赋值需要前一个容量的背包配合。正序遍历的话,背包大的会重复存入背包小的物品。

总结:

        dp定义及含义:dp[j]表示背包容量为j的最大价值和

        状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])

        初始化:dp[0] = 0 因为放不下物品

        遍历顺序:先遍历物品,再倒序遍历背包容量

        dp[w]即为所要求的值。

①、分割等和子集

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

事例:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

思路:

        将该题转化为01背包问题,此时nums表示物品,nums[i]既是物品重量也是物品价值,求等和子集,也就是求总和一半的子集。转化为背包问题,也就是求物品是否能填饱容量为总和一半的背包。

步骤:

        将nums排序,求得总和一半target。创建数组dp[target + 1],初始化背包,在每行初始化遍历完后判断dp[target]是否等于target,若等于,则返回true,若不等,则返回false。因为背包问题中的状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i]),可能造成溢出,故每行物品遍历中判断dp[target]。

动态规划:  

dp定义及含义:

        dp[j]表示背包容量为j的最大价值和

        状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])

        初始化:dp[0] = 0 因为放不下物品

        遍历顺序:先遍历nums,再倒序遍历背包容量

        判断dp[target] == target

代码:

 public boolean canPartition(int[] nums) {
        Arrays.sort(nums);
        int target = 0;
        for(int x : nums){
            target += x;
        }
        if(target % 2 != 0) return false;
        target = target / 2;
        //二维数组
        // int[][] dp = new int[nums.length][target + 1];
        // for(int i = 0;i < nums.length;i++){
        //     dp[i][0] = 0;
        // }
        // for(int j = 0;j <= target;j++){
        //     if(nums[0] <= j) dp[0][j] = nums[0];
        // }
        // for(int i = 1;i < dp.length;i++){
        //     for(int j = 1;j < dp[0].length;j++){
        //         if(nums[i] <= j){
        //             dp[i][j] = Math.max(dp[i - 1][j - nums[i]] + nums[i],dp[i - 1][j]);
        //         }else{
        //             dp[i][j] = dp[i - 1][j];
        //         }
        //     }
        //     if(dp[i][dp[0].length - 1] == target) return true;
        // }

        //一维数组
        int[] dp = new int[target + 1];
        dp[0] = 0;
        for(int i = 0;i < nums.length;i++){
            for(int j = target;j > 0;j--){
                if(j >= nums[i]){
                    //可以存放
                    dp[j] = Math.max(dp[j],dp[j - nums[i]] + nums[i]);
                }
            }
            if(dp[target] == target) return true;
        }
        return false;
    }

参考:代码随想录 (programmercarl.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值