动态规划之01背包问题和钢条切割问题(完全背包)

提个问题

背包问题与钢条切割 ,实际上是01背包和完全背包问题

  • 01背包问题的最优子结构是:放还是不放,dp[i][j] ==>二维数组,记录了不同容量放不同物品价格的数据 。
  • 钢条切割问题最优子结构:切还是不切,f[j] ==>一维数组,j代表长度相当于容量,只记录了不同容量的最优解数据。

在这里插入图片描述
更新2023/11/10
有没有发现一点不一样,钢条切割方法跟平常的一维数组背包问题解法不一样,就是双重循环,钢条切割是容量在外循环.
使用一维数组解决背包问题,01和完全。
问题1:为什么01背包问题,容量必须在内循环。

  • 一般来说01背包问题容量如果在外循环,所导致的问题就是在背包每一个容量都会尝试放入所有的物品。但关键是动态规划解决背包问题是利用重复子问题的解解决问题,在你容量3的时候已经放入物品2,然后在容量4的时候你利用容量3的解的基础上再放入物品2这不就重复放入物品2了吗。所以理解了这个我们还可以解决一个问题—》问题2。

问题2: 完全背包问题容量和物品的内外循环顺序可以调换吗

  • 可以调换,详情看问题1

问题3: 01背包问题容量内循环为什么从大到小,而完全背包问题从小到大。

  • 这个问题非常关键,是解决完01背包问题 然后 如何解决完全背包问题。01背包问题使用一维数组时,容量内循环需要 for( int j=bagSize;;j–),容量由大到小,这是为了防止重复放入问题不能j++ 。可以看代码随想录。 理解了这个我们清楚,如果不是容量从大到小那么,会出现这个问题,当物品2的时候,解决容量5问题,递推公式是max{dp[5],dp[5-weight[2] ]+value[i2] }, 这个时候递推公式需要容量3,如果我们从小到大遍历容量,那么此时容量3已经添加了物品2,递推公式中dp[5-weight[2] ]+value[i2] 就错误了,同时如果每次容量从小到大遍历,容量3添加了物品2,容量5也添加了物品2,这个不就是重复添加物品吗,物品只有一个在01背包问题。
  • 但是请想想,完全背包问题与01背包问题不同点在于每一个物品有数量无限,01背包每个物品只有一个。重复添加不就是正好解决了物品有无限个吗。误打误撞是吧。
    在这里插入图片描述

期中考试题钢条切割,一眼背包问题结果写错了在这里插入图片描述

钢条切割问题:

给定一段长度为n 英寸的钢条和一个价格表Qj,长度表为Lj,价格表和长度表相互关联。求切割钢条方案,使得销售收益F(n)最大。
网上的图,长度表L为长度i,价格表Q为价格pi。

 int[] L={0,1,2,3,4,5,6,7,8,9,10};
 int[] Q={0,1,5,8,9,10,17,17,20,24,30};

在这里插入图片描述
利用动态规划,自底向上解决这个问题。

切割长度问题:

  • 切割:n的长度,分成不同长度的价值,最大长度.Lj为长度,Qj为价格
    * 最优子结构:切与不切
    * f(n)= max{f(n-Lj)+Q(Lj),f(n) }
  • 问题1:错误的最优子结构==》f(n)= max{ f(n-Lj)+f(Lj) , f(n) }
    解析: 最优子结构:在的这部分==》正确是f(n-Lj)+Q(Lj),切了之后,被切的这部分的长度还可以再细分成不同长度所以用了f(Lj)没用Q(Lj),然后答案就错了。这部分是最优子结构思想出了问题,这里表示的是我每一次切的长度,什么意思,我们看一下 f(n-Lj)+f(Lj) 这部分中Lj表示的是集合(从j=0…),表达的意思是长度切多少不确定,那就遍历取最大,所以必须用Q(Lj)表示每一次切的长度的价格。所以最优子结构其实可以这样 max{max{ f(n-Lj)+Q(Lj) } ,f(n) }

代码

  public static void main(String[] args){
        int[] L={0,1,2,3,4,5,6,7,8,9};
        int[] Q={0,1,5,8,9,10,17,17,20,24};
        int n=8;
        int[] f = cut(n, Q, L);
        for(int i=L[1];i<=n;i++)
        System.out.println("长度为"+i+"价值:"+f[i]);

    }

    /**
     * 切割长度问题:
     * 切割:n的长度,分成不同长度的价值,最大长度.Lj为长度,Qj为价格
     * f(n)= max{f(n-Lj)+Q(Lj),f(n-Lj) }
     *
     *问题:错误的最优子结构==》f(n)= max{f(n-Lj)+f(Lj),f(n-Lj) }
     * @param n
     * @param Q
     * @param L
     * @return
     */
     static int[] rod_cutting(int n,int[] Q,int[] L){

        int[] f=new int[n+1];

        for(int i=L[0];i<=n;i++){//容量
  
            for(int j=1;j<L.length;j++){//钢条长度
            if(L[j]<=i)
                f[i]=Math.max(f[i], f[i-L[j]]+Q[L[j]]);
            }
         }



    return  f;
    }

01背包问题

代码

public static void main(String[] args){
        int[] weight={2,1,3,2};
        int[] value={12,10,20,15};
        int bagsize=5;
//        int[] weight = {1,3,4};
//        int[] value = {15,20,30};
//        int bagsize = 4;

//        int bagsize = 100;
//
//        int[] weight = {18,42,88,3};
//        int[] value ={141,136,192,223};

        testWeightBagProblem(weight, value, bagsize);

    }

    /**
     * 动态规划获得结果
     * @param weight  物品的重量
     * @param value   物品的价值
     * @param bagSize 背包的容量
     */
    public static void testWeightBagProblem(int[] weight, int[] value, int bagSize) {
        // 递推公式:max{dp[i-1][j],dp[i-1][j-weight[i]]+value[i]}
        // 动态规划:自下而上==> 需要初始化 对于背包问题的初始化,
        //                      根据递推公式,dp[i][j]由 max{dp[i-1][j],dp[i-1][j-weight[i]]+value[i]}得出,
        //                      也就是之前的dp[i-1][j]或者[i-1]dp[i-1][j-weight[i]]+value[i] 得到,所以dp[i][j]需要初始化dp[i-1][ j从0到最大值 ]
        //                      一直往前推直到i=0时不能往前推了,初始化i=0那一行

        int[][] dp=new int[weight.length][bagSize+1];

        for(int j=weight[0];j<=bagSize;j++){
            dp[0][j]=value[0];
        }

        for(int i=1;i<weight.length;i++){
            for(int j=0;j<=bagSize;j++){
                if(j<weight[i]) 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]);
            }
        }

        for(int i=0;i<weight.length;i++){
            for (int j=0;j<=bagSize;j++)
                System.out.print(dp[i][j]+"\t");
                System.out.println();
        }

        System.out.println("背包问题:最大的价值的是=》"+dp[weight.length-1][bagSize]);
    }
  
  //一维数组版本
 public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagWeight + 1];
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for (int j = 0; j <= bagWeight; j++){
            System.out.print(dp[j] + " ");
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值