背包问题及其优化

0-1背包:

0-1背包(滚动数组优化)

c++

python:

完全背包:

完全背包——恰好装满求最小值

多重背包:

多重背包(转化成0-1背包,再二进制分解优化)第二层循环要从大到小

二进制拆解即:7 = 2^0+2^1+2^2

多重背包 (单调队列优化)难!

0-1背包求最优方案数:https://www.acwing.com/problem/content/11/

 

求方案数,转化为01背包:

https://leetcode-cn.com/problems/target-sum/

传统回溯做法:

class Solution {

    private int tt,ans,n;
    public void dfs(int[] nums,int summ,int i){
        if(i==n-1){
            if(summ==tt){
                ans++;
            }
            return ;
        }
        dfs(nums,summ+nums[i+1],i+1);
        dfs(nums,summ-nums[i+1],i+1);
        
    }
    public int findTargetSumWays(int[] nums, int target) {
        tt = target;
        n = nums.length;
        dfs(nums,nums[0],0);
        dfs(nums,-nums[0],0);
        return ans;
    }
}

记数组的元素和为sum,添加- 号的元素之和为neg,则其余添加+ 的元素之和为sum−neg,得到的表达式的结果为
(sum−neg)−neg=sum−2⋅neg=target

neg = (sum-target)/2
 

由于数组 nums 中的元素都是非负整数,neg 也必须是非负整数,所以上式成立的前提是 sum−target 是非负偶数。若不符合该条件可直接返回 0。

若上式成立,问题转化成在数组 nums 中选取若干元素,使得这些元素之和等于 neg,计算选取元素的方案数。我们可以使用动态规划的方法求解。

定义二维数组 dp,其中 dp[i][j] 表示在数组 nums 的前 i 个数中选取元素,使得这些元素之和等于 j 的方案数。假设数组 nums 的长度为 n,则最终答案为 dp[n][neg]。

当没有任何元素可以选取时,元素和只能是 0,对应的方案数是 1,因此动态规划的边界条件是:
dp[0][j] = 1,j=0  ;dp[0][j]=0,j>=1;

当 1≤i≤n 时,对于数组nums 中的第 ii 个元素 num(i的计数从 1 开始),遍历 0≤j≤neg,计算 dp[i][j] 的值:

如果j<num,则不能选 num,此时有dp[i][j]=dp[i−1][j];

如果j≥num,则如果不选 num,方案数是dp[i−1][j],如果选num,方案数是dp[i−1][j−num],此时有 dp[i][j]=dp[i−1][j]+dp[i−1][j−num]。

因此状态转移方程如下:
dp[i][j]={ 
dp[i−1][j],j<nums[i]
dp[i−1][j]+dp[i−1][j−nums[i]],j≥nums[i]

}

最终得到 dp[n][neg] 的值即为答案。

class Solution {

    
    public int findTargetSumWays(int[] nums, int target) {
        int sum=0,neg=0;
        int n = nums.length;
        for(int i=0;i<n;i++){
            sum+=nums[i];
        }
        int diff = sum-target;
        neg = diff/2;
        if(diff%2!=0||diff<0){
            return 0;
        }
        //neg = (sum-target)/2;
        int[][] dp = new int[n+1][neg+1];
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=neg;j++){
                dp[i][j] = dp[i-1][j];
                if(j>=nums[i-1]){
                    dp[i][j]+=dp[i-1][j-nums[i-1]];
                }
            }
        }
        return dp[n][neg];
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值