背包问题及应用

0-1背包问题

有一个容量为w的背包,有x件物品,物品的重量为weight[i],价值为value[i],每件物品只有一个,问怎样装物品才能使背包里物品的价值最大。

1、二维数组写法

1、确定dp数组含义:dp[i][j]表示背包容量为j时,装0-i之间的物品所能获取到的最大价值
2、确定递推公式:
   当该物品的重量比背包容量大时,不能装该物品。dp[i][j] = dp[i-1][j];  背包最大价值等于背包容量为j时,装到i-1号物品时的最大价值。
   当该物品可以装进去时,dp[i][j] = Math.max(dp[i-1][j],dp[i-1][ j-weight[i] ]+value[i]); 此时背包的最大价值为:背包容量为j,不装该物品时背包的价值;dp[i-1][ j-weight[i] ](背包装到i-1号物品,背包容量为j-weight[i] 时背包的最大价值。因为可以装下该物品,就取装到i-1号物品时,装物品i正好填满当前背包的容量j,就是j-weight[i])有点难理解
3、初始化
  当背包容量为0时,背包的价值为0,也就是dp[i][0]等于0;
 只 装0号物品时,背包的最大价值为0号物品的价值。dp[0][j] = value[0];(前提是背包容量大于0号物品的重量)
4、遍历顺序:
  从第一个物品开始遍历,依次遍历完所有的物品

public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        //背包最大容量
        int bagsize = 4;
        int [][] dp = method(weight,value,bagsize);
        for(int [] arr : dp){
            for(int i : arr)
                System.out.print(i+" ");
            System.out.println();
        }  
    }
    public static int[][] method(int []weight,int []value,int bagsize){
        //dp[i][j] 在0-i号物品中任选,装入容量为j的背包,最大价值
        int [][]dp = new int[weight.length][bagsize+1];
        // 初始化,当背包容量为0时,物品价值应该为0
        for(int i = 0;i<dp.length;i++){
            dp[i][0] = 0;
        }
        //初始化,先把0号物品放入各个容量情况下的背包。
        for(int i = 0;i<dp[0].length;i++){
            if(weight[0] <= i) dp[0][i] = value[0];
            else dp[0][i] = 0;
        }
        for(int i = 1;i<dp.length;i++){
            for(int j = 1;j<dp[0].length;j++){
                if(weight[i] > j) dp[i][j] = dp[i-1][j];
                else dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
            }
        }
        return dp;
    }

上面的二维数组可以优化成一维数组,但是背包容量要从大往小开始遍历。

public static int[] method2(int[] weight,int[] value,int bagsize){
        //背包容量为j时,背包的最大价值
        int dp[] = new int[bagsize+1];
        //背包容量为0时,背包最大价值为0
        dp[0] = 0;
        for(int i = 0;i<weight.length;i++){
            for(int j = bagsize;j>=weight[i];j--){
                dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);
            }
        }
        return dp;
    }

0-1背包问题的应用

分割等和的子集

在这里插入图片描述
把数组中的数分割成两个等和的子集,说明每个子集的和等于数组中所有数的总和sum的一半,所以转化为背包问题为:
1、背包的体积为sum / 2
2、背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
3、背包如果正好装满,说明找到了总和为 sum / 2 的子集。
4、背包中每一个元素是不可重复放入。
 题目中只是分割成两个等和的子集,若从其中选出一些数的和等于总和的一半。那么另外一些数的总和一定等于数组总和的一半。

 public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int x:nums){
            sum += x;   
        }
        if(sum%2 != 0) return false;
        int target = sum/2;

        int dp[] = new int[target+1];
        for(int i = 0;i<nums.length;i++){
            for(int j = dp.length-1;j>=nums[i];j--){
                dp[j] = Math.max(dp[j],dp[j-nums[i]] + nums[i]);
            }
        }
        return dp[dp.length-1] == target;

    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值