分析01背包问题

问题描述:

  一个旅行者有一个最多能装C公斤的背包,现在有n件物品,每件的重量分别是W1、W2、……、Wn,每件物品的价值分别为V1、V2、……、Vn, 需要将物品放入背包中,要怎么样放才能保证背包中物品的总价值最大?

思路分析:
首先,这个问题是一个求极值问题,求最大值价值,也即是求最大值。那么之后可以看这个问题是否具有最优子结构。本来是要求n件物品装入容量为C的背包的最大价值,如果我们知道了n-1件物品装入容量为C的背包的最大价值,能不能推得n件物品装入容量为C的背包的最大价值,感觉好像是可以的(具体状态转移方程等会再详细分析)。接下来看各个子问题是否有重合,我觉得其实这一步最好是在推出状态转移方程之后再进行。如果要用动态规划,找不出状态转移方程,根本就没法得到最终需要求的解,所以我觉得分析状态转移方程是重点。
接下来就试着分析状态转移方程:
首先明确我们要求的问题:n件物品装入容量为C的背包的最大价值。那么可以怎么划分子问题,一种方式是考虑m(m<n)件物品装入容量为C的背包的最大价值,将其作为子问题,我们如果能得到这些子问题的最优解,最终解就可以通过这些子问题的最优解得到。那么该怎样描述各个状态呢?其实分析一下这个问题就可以看出,决定物品装入背包的最大价值的关键因素就是:背包的总容量到底是多大以及这些物品的重量和价值分别是多少,因此我们可以提炼出两个量表示这类问题,一个当然就是背包容量,另一个就是待装入物品的数量(有了待装入物品的数量,自然可以得到它们分别的价值和重量)。因此我们用一个二元组<i,j>(前i件物品装入容量为j的背包的最大价值)来描述各个问题的状态,找出对应的最优解,用数组result[i][j]表示。现在我们就完成了划分子问题和状态表示两步,之后就是推出状态转移方程。
假设我们已经求出前i-1件物品装入容量j的背包的价值总和最大值为result[i-1][j],固定容量j的值不变,则对第i件物品的装法讨论如下:
首先第i件物品的重量w[i]必须小于等于容量j才行,即
1、若w[i]>j,则第i件物品肯定不能装入容量为j的背包,此时result[i][j]=result[i-1][j]
2、若w[i]<=j,则首先明确的是这件物品是可以装入容量为j的背包的,那么如果我们将该物品装入,则有 result[i][j]=result[i-1][j-c[i]]+v[i] ,需要注意的是,这里求将第i件物品装入容量为j的背包的最大价值时,必须根据将前i-1个物品装入容量为j-c[i]的背包的最大价值来得到,这点很重要。
随之而来的问题是我们要判断第i件物品装到容量为j的背包后,背包内的总价值是否是最大?那么就要对比装入第i件物品和不装入第i件物品哪种方式得到的总价值更大,即如果装了第i件物品后的总价值result[i-1][j-c[i]]+v[i]大于没装之前的总价值最大值result[i-1][j],则应该装入第i件物品;反之则说明第i件物品不必装入容量为j的背包。
故,状态转移方程如下:
result[i][j] = max(result[i-1][j-c[i]]+v[i],result[i-1][j])

注意:这里的前i件物品是给定次序的。
一种实现方式如下:

    public static int MySolver(int capacity,int[] weight,int[] value){
        int allthingsnum = weight.length;
        //状态表示,用一个二维数组result[i][c]表示前i件物品能装入容量为c的背包中能得到的物品价值总和的最大值,这样就表示了子问题的最优解
        int[][] result = new int[allthingsnum+1][capacity+1];
        //初始状态是什么?
        for (int i = 0;i <= allthingsnum;i++){
            result[i][0] = 0;
        }
        for (int j = 0;j <= capacity;j++){
            result[0][j] = 0;
        }
        for (int j = 1;j <= capacity;j++){//背包容量应该怎样取值
            for (int i = 1;i <= allthingsnum;i++){
                if (weight[i-1] > j){//如果第i个物品数量大于此时的背包容量,那么这个物品放不进背包,这里需要注意weight数组和result数组索引的含义不同,需要减一
                    result[i][j] = result[i-1][j];
                }else {//如果第i件物品可以放入背包
                    result[i][j] = Math.max(result[i-1][j-weight[i-1]]+value[i-1],result[i-1][j]);
                }
            }
        }
        int myresult = result[allthingsnum][capacity];
        //下面的程序是为了输出选出的商品编号
        int j = capacity;
        String numstr = "";
        for (int i = allthingsnum;i > 0;i--){
            //如果result[i][j]>result[i-1][j],这说明第i件物品是放入背包的
            if (result[i][j] > result[i-1][j]){
                numstr = numstr + i + " ";
                j -= weight[i-1];
            }
            if (j==0)
                break;
        }
        System.out.println("选中的商品编号为:"+numstr);
        return myresult;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值