动态规划·0-1背包问题

作为动态规划的典型问题,0-1背包思路适配的地方很多,网上很多版本,个人最能看明白的是carl的代码随想录,里面从二维到一维到dp数组,起码讲得还算能应付做题,毕竟算法这个东西,有的人需要很明白,有的人就稍微背背就行。

问题本身

首先我们要知道的是:0-1背包问题本身。

有N个不同重量的物品分别拥有重量weight[N],它们的价值分别为value[N]

现在给你一个背包,背包容量---即能装下总重量为bagSize的物品,问这个背包能装的最大价值。

首先我们能确定的是:对于每一个背包重量,最大价值是唯一的。

其次需要我们意识到问题服务的对象是:容量大小为X的背包里的最大价值

dp[]:

背包容量01234567
最大价值

物品重量ABCDEFGH
物品价值abcdefgh

我们把第一个表格定义为dp数组,其大小就是背包容量,我们希望根据顺序的规则,对数组操作,并将结果“容量大小为X的背包里的最大价值”记录在数组的最后一个元素。

那么背包问题遵从什么规则呢?

由于是从前往后的顺序构造,也就是dp[i]比dp[j](i < j)要早得到。

那这个求dp[N]问题是不是转换成:

                                     已知dp[0]~dp[N-1],求dp[N]?

观察这个过程:  当有一个物品重量为A,价值为a,当前有一个背包,大小为N;已知当背包大小为N-A(N-A<N)时的最大价值为V1-------------->大小为N的背包的价值(把A装进来的情况)就是a+V1。

如果有很多物品,则需要遍历这些物品,然后取得最大值,就是背包大小为N的最大价值。

因此将遍历过程写成代码:(结合文字看才有感觉)

        

for(int i = 0;i < 物品个数;i++){//这一层是遍历所有物品
    for(int j = 最大重量;j >= 物品重量;j--){//这一层遍历的是不同物品的价值
        dp[j] = Max(dp[j],dp[j-物品重量]+物品价值)

    }
}

其中dp[j]本身会有一个初始值,这个初始值需要稍微根据题目来思考下。    

当会写出一维数组的情况时,那就不要担心二维的了,二维的更直观简单。

                      

一道例题

        

这道题不难,考验的是问题的转化能力,怎么把这个符号求和问题转化为0-1背包问题?

1:不同的符号不要紧,只要确定了一堆是正数,那另一堆一定是负数,只考虑正数X就可以解题

2: 有固定的目标和,由加减关系:X-(sum-X)=target 得到所有正数的和X

3.问题转化为:在所有数(物品)中找出加和为X(背包大小)的方案数量(价值)。

好了,个人觉得可以贴代码了

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        //先对数组中的元素求和
        int sum = 0;
        for(int i = 0;i < nums.length;i++)sum+=nums[i];
        //这是一些边界
        if(target>0&&sum<target)return 0;
        if(target<0&&sum<-target) return 0;
        if((target+sum)%2==1)return 0;
        //根据求和信息和target的关系,将数组分为正负两部分
        //也就可以求出取正数的那部分为size
        int size = (sum+target)/2;
        //将问题转化为在nums中取出任意个数,使它们的和为size
        //这就是一个典型的0-1背包问题
        if(size<0)size = -size;
        int []dp = new int[size+1];
        //dp[j]表示总和为j的元素组合方案个数为dp[j]
        dp[0] = 1;
        //需要遍历的是整个nums,以作为滚动数组
        //本质的含义是将二维的dp数组铺平展开,逐层累加
        for(int i = 0;i < nums.length;i++){
            for(int j = size;j>=nums[i];j--){
                dp[j] =dp[j-nums[i]]+dp[j];
            }
        }
        return dp[size];
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值