01背包及其衍生问题(动态规划)

01背包指在背包容量确定的情况下,有不同种类的商品,商品的价值和重量各不相同,问怎么拿商品能使拿出的商品总价值最大,商品总重量不得超过背包容量。

该题使用动态规划求解,定义w[i]为i商品的重量,v[i]为i商品的价值。定义dp[i][j] 用于保存在背包容量为j时,前i个商品中所能拿到的最优解,即总价值最大解。

在这里我们不考虑商品i之后的商品是否获取(商品排列顺序随意,最后都会遍历到)。则只有两个判断条件,即第i个商品拿还是不拿?

第一:如果当前背包容量j小于i商品的重量w[i],那肯定是拿不了的,所以,这时的dp[i][j]与i商品无关,其只能等于当背包重量为j时,前i-1商品中的最优解,即公式为dp[i][j] = dp[i-1][j]。

第二:(1)如果当前背包容量j >= i商品的重量w[i],那可以考虑是否拿i商品,如果拿,则背包的剩余容量就是j-w[i],然后再用这剩余的容量去装前i-1种商品,则可知这样的最有解应为dp[i][j] = dp[i-1][j-w[i]] + v[i]。

(2)如果不拿i商品,则和一情况一样,dp[i][j] = dp[i-1][j]。

        所以在当前背包容量j >= i商品的重量w[i]时只有这两种分支,取其中最大值,即dp[i][j] = Math.max(dp[i-1][j-w[i]] + v[i],dp[i][j] = dp[i-1][j])。

import java.util.Scanner;

public class Test {
     public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);// 假定是6个物品
            int[] goodsworth = new int[6];
            int[] bagweigt = new int[6];
            for (int i = 0; i < 6 ; i++) {
                goodsworth[i] = sc.nextInt();
                bagweigt[i] = sc.nextInt();
            }
            int[][] dp = new int[7][9]; // 定义背包容量为8
         for (int i = 1; i < 7; i++) {
             for (int j = 0; j < 9; j++) {
                 if(bagweigt[i-1] > j){
                     dp[i][j] = dp[i-1][j]; //背包容量不足,拿不了i商品
                 }else {
                     dp[i][j] = Math.max(dp[i-1][j-bagweigt[i-1]]+goodsworth[i-1],dp[i-1][j]); //取拿i商品和不拿i商品两种情况的最大值。
                 }
             }

         }
         System.out.println(dp[6][8]);
     }


    }







关于01背包问题,该文讲解非常好

关于01背包问题,牛客网有一道变形题:

 本题和01背包问题差别不大,无非是增加了从属关系,从属关系中主物品最多有2个附属物品,则变化的情况变为了,在拿主物品时,判断是否要拿从属物品,即4中情况:(1)只拿主物品 ;(2)拿主物品和副物品1 ;(3)拿主物品和副物品2 ;(4)拿主物品和副物品1和副物品2 ;

在这4个中寻找最大值,动态规划过程仍和上述一样,如果拿则dp[i][j] = Math.max(dp[i-1][j-w[i]] + v[i],dp[i][j] = dp[i-1][j])。不拿则dp[i][j] = dp[i-1][j]。代码如下

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String[] str = sc.nextLine().split(" ");
        int money = Integer.parseInt(str[0]);
        int count = Integer.parseInt(str[1]);
        if(count<=0||money<=0) System.out.println(0);
        
        goods[] gd = new goods[count+1];
        for(int i = 1; i <= count; i++){
            int v = sc.nextInt();
            int p = sc.nextInt();
            int q = sc.nextInt();
            gd[i] = new goods(v, p, q);
            
        }
        for(int i = 1; i <= count; i++){ //这段不能和上面合并,如果合并,在副商品添加时若主物品没有添加,则会报空指针异常。因为此时主物品位置处没有对象,值为null。
            if(gd[i].q > 0){
                if(gd[gd[i].q].a1 == 0)
                    gd[gd[i].q].setA1(i);
                else{
                    gd[gd[i].q].setA2(i);
                }
            }
        }
        
        int[][] dp = new int[count+1][money+1];
        for (int i = 1; i <= count; i++) {
            int v=0,v1=0,v2=0,v3=0,tempdp=0,tempdp1=0,tempdp2=0,tempdp3=0;
 
            v = gd[i].v;
 
            tempdp = gd[i].p*v; //只有主件
 
            if(gd[i].a1!=0){//主件加附件1
                v1 = gd[gd[i].a1].v+v;
                tempdp1 = tempdp + gd[gd[i].a1].v*gd[gd[i].a1].p;
            }
 
            if(gd[i].a2!=0){//主件加附件2
                v2 = gd[gd[i].a2].v+v;
                tempdp2 = tempdp + gd[gd[i].a2].v*gd[gd[i].a2].p;
            }
 
            if(gd[i].a1!=0&&gd[i].a2!=0){//主件加附件1和附件2
                v3 = gd[gd[i].a1].v+gd[gd[i].a2].v+v;
                tempdp3 = tempdp + gd[gd[i].a1].v*gd[gd[i].a1].p + gd[gd[i].a2].v*gd[gd[i].a2].p;
            }
 
            for(int j=1; j<=money; j++){
                if(gd[i].q > 0) {   //当物品i是附件时,相当于跳过
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = dp[i-1][j]; //为保证dp[i][j]一定有值而不是默认的0.
                    if(j>=v&&v!=0) dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-v]+tempdp);
                    if(j>=v1&&v1!=0) dp[i][j] = Math.max(dp[i][j],dp[i-1][j-v1]+tempdp1);
                    if(j>=v2&&v2!=0) dp[i][j] = Math.max(dp[i][j],dp[i-1][j-v2]+tempdp2);
                    if(j>=v3&&v3!=0) dp[i][j] = Math.max(dp[i][j],dp[i-1][j-v3]+tempdp3);
                }
            }
        }
        System.out.println(dp[count][money]);
        
        
    }
     private static class goods{
         public int v;//物品的价格
         public int p;//物品的重要度
         public int q;//物品的主附件ID
         public int a1 = 0;//附件1ID
         public int a2 = 0;//附件2ID
         
         public goods(int v, int p, int q){
            this.v = v;
            this.p = p;
            this.q = q;
         }
         
         public void setA1(int a1){
             this.a1 = a1;
         }
         
         public void setA2(int a2){
             this.a2 = a2;
         }
     }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值