01背包——基于java实现

一、0/1背包的概念

有n个物品,它们有各自的价值和体积,现有给定容量的背包,如何让背包装入的物品的价值最大?


 如:假设你的背包容量为10,物品的重量和价值如下,且每个物品只有一个,怎么样拿才能使你背包的价值最大呢?

编号:1 234
物品重量:2355
物品价值:2437

xi代表第i个珠宝的选择(xi = 1 代表选择该珠宝,0则代表不选),vi代表第i个珠宝的价值,wi代表第i个珠宝的重量。于是我们就有了这样的限制条件:

x_{1}w_{1}+x_{2}w_{2}+x_{3}w_{3}+x_{4}w_{4}\leqslant capacity(背包容量)

2x1+3x2+5x3+5x4≤10

 二、解决思路

初始状态是背包容量为10,背包内物品总价值为0

①对于1号珠宝,当前容量为10,容纳它的重量2绰绰有余,因此有两种选择,选它或者不选

② 继续进行选择,如果我们选择了珠宝1,那么对于珠宝2,当前剩余容量为8,大于珠宝2的容量3,因此也有两种选择,选或者不选。

 

③最后如图 

本来应该有16个待选结果,但有三个结果由于容量不足以容纳下最后一个珠宝,所以就没有继续进行裂变。

然后,我们从这些结果中,找出价值最大的那个,也就是13,这就是我们的最优选择,根据这个选择,依次找到它的所有路径,便可以知道该选哪几个珠宝,最终结果是:珠宝4,珠宝2,珠宝1。

 三、递推关系

定义函数KS(i,j):代表当前背包剩余容量为j时,前i个物品最佳组合所对应的价值;

  1. 背包剩余容量不足以容纳该物品,此时背包的价值与前i-1个物品的价值是一样的,KS(i,j) = KS(i-1,j)
  2. 背包剩余容量可以装下该商品,此时需要进行判断,因为装了该商品不一定能使最终组合达到最大价值,如果不装该商品,则价值为:KS(i-1,j),如果装了该商品,则价值为KS(i-1,j-wi) + vi,从两者中选择较大的那个,所以就得出了递推关系式:

 四、实现代码

1.递归

这里为了方便处理,将数组ws和vs都增加了一个补位数0,防止数组越界,输出结果


public class Solution{
    int[] vs = {0,2,4,3,7};
    int[] ws = {0,2,3,5,5};

    @Test
    public void testKnapsack1() {
        int result = ks(4,10);
        System.out.println(result);
    }

    private int ks(int i, int c){
        int result = 0;
        if (i == 0 || c == 0){
            // 初始条件
            result = 0;
        } else if(ws[i] > c){
            // 装不下该珠宝
            result = ks(i-1, c);
        } else {
            // 可以装下
            int tmp1 = ks(i-1, c);
            int tmp2 = ks(i-1, c-ws[i]) + vs[i];
            result = Math.max(tmp1, tmp2);
        }
        return result;
    }
}

2.动态规划

当i=1时,即只有珠宝1可供选择,那么如果容量足够的话,最大价值自然就是珠宝1的价值了。

当i=2时,有两个物品可供选择,此时应用上面的递推关系式进行判断即可。这里以i=2,j=3为例进行分析: 

最终表格 

这样,我们就得到了最后的结果:13。根据结果,我们可以反向找出各个物品的选择,寻找的方法很简单,就是从i=4,j=10开始寻找,如果ks(i-1,j)=ks(i,j),说明第i个物品没有被选中,从ks(i-1,j)继续寻找。否则,表示第i个物品已被选中,则从ks(i-1,j-wi)开始寻找。 

代码:


public class Solution{
    int[] vs = {0,2,4,3,7};
    int[] ws = {0,2,3,5,5};
    Integer[][] results = new Integer[5][11];
    
    @Test
    public void testKnapsack3() {
        int result = ks3(4,10);
        System.out.println(result);
    }

    private int ks3(int i, int j){
        // 初始化
        for (int m = 0; m <= i; m++){
            results[m][0] = 0;
        }
        for (int m = 0; m <= j; m++){
            results[0][m] = 0;
        }
        // 开始填表
        for (int m = 1; m <= i; m++){
            for (int n = 1; n <= j; n++){
                if (n < ws[m]){
                    // 装不进去
                    results[m][n] = results[m-1][n];
                } else {
                    // 容量足够
                    if (results[m-1][n] > results[m-1][n-ws[m]] + vs[m]){
                        // 不装该珠宝,最优价值更大
                        results[m][n] = results[m-1][n];
                    } else {
                        results[m][n] = results[m-1][n-ws[m]] + vs[m];
                    }
                }
            }
        }
        return results[i][j];
    }
}


class Solution{
    public static void main(String[] args) {
        int[] v = {2,4,3,7};
        int[] w = {2,3,5,5};
        int capaticy =10;
       int dp[][]=new int[v.length+1][capaticy+1];
        for (int i = 1; i < v.length+1; i++) { //物品的个数
            for (int j = 0; j < capaticy+1; j++) { //容量
                if (w[i-1]>j){
                    //装不下,为什么是w[i-1]而不是w[i],是因为我用i代表物品的个数,i=0时,表示没有物品可选择,自然数组都为0
                    // i=1时,表示存在一个物品,对应数组的下标0,所以是w[i-1]
                    dp[i][j]=dp[i-1][j];
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1]);
                }
            }

        }
        for (int i = 0; i <v.length+1 ; i++) {
            for (int j = 0; j <capaticy+1 ; j++) {
                System.out.print(dp[i][j]+" ");
            }
            System.out.println();

        }

    }

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值