背包问题(完全背包)

动态规划合集:

1.矩阵链乘法
2.投资组合问题
3.完全背包问题
4.01背包问题
5.最长公共子序列

例题3——背包问题

描述

一个背包,可以放入n种物品,物品j的重量和价值分别为,如果背包的最大重量限制是b,怎么样选择放入背包的物品以使得背包的总价值最大?

组合优化问题,设表示装入背包的第j个物品的数量,解可以表示为。那么目标函数和约束条件是:

如果组合优化问题的目标函数和约束条件都是线性函数,称为线性规划。如果线性规划问题的变量都是非负整数,则称为整数规划问题。背包问题就是整数规划问题。限制所有的时称为0-1背包

子问题的界定(就是靠什么来划分子问题):由参数k和y界定

k:考虑对物品1,2,3,...,k的选择。

y:表示背包总重

子问题计算顺序:k=1,2,...,k,对给定的k,y=1,2,...,b

:装前k个物品,重量不超过y时的背包最大值。
\begin{array} {l}{F_{k}(y)=\max \left\{F_{k-1}(y), F_{k}\left(y-w_{k}\right)+v_{k}\right\}} \\ {F_{0}(y)=0, \quad 0 \leq y \leq b, \quad F_{k}(0)=0, \quad 0 \leq k \leq n} \\ {F_{1}(y)= \lfloor\frac{y}{w_{1}}\rfloor v_{1}, \\ F_{k}(y)=-\infty \quad y<0.} \end{array}
包含两种情况:不装第k种物品或至少装一件第k种物品。

对的解释:装一件第k种物品后,最优的解法仍然是在前k个物品进行选择,仍有可能再选入1件第k种物品。

对边界条件:

:即只用第一种物品背包重量限制为y的最大价值,为了保证背包不超重,第一种物品至多能装个,因为背包价值为

有些那么通过设置为负无穷,在选择过程中抛弃掉为负的情况。

标记函数:用来追踪解
i_k(y):装前k种物品,总重不超过y,背包达到最大值时装入物品的最大标号 \\ i_{k}(y)=\left\{\begin{array}{ll}{i_{k-1}(y)} & {F_{k-1}(y)>F_{k}\left(y-w_{k}\right)+v_{k}} \\ {k} & {F_{k-1}(y) \leq F_{k}\left(y-w_{k}\right)+v_{k}}\end{array}\right.\\ i_{1}(y)=\left\{\begin{array}{ll}{0} & {y<w_{1}} \\ {1} & {y \geq w_{1}}\end{array}\right.

实例

按照递推公式:以k=2为例子,简单演算如下:

依次类推,可得备忘录表:

背包问题(完全背包)_第1张图片
备忘录表

标记函数的备忘录:

背包问题(完全背包)_第2张图片
标记函数表

关于背包问题的总结

物品受限背包:第i种物品最多用个

0-1背包问题

多背包:m个背包,背包装入最大重量在满足所有背包重量约束下使物品价值最大。

二维背包:每件物品重量和体积,背包总重不超过b,体积不超过V,使得物品价值最大。

代码实现

此问题是完全背包问题,即 一个物品可重复出现。

public class knapsackProblem {
    public static int[][]mem; // 备忘录表
    public static int[][]s; // 标记函数表
    public static void main(String[] args) {
        int n = 4;
        int d = 10;
        int []w = {2,3,4,7};
        int []v = {1,3,5,9};

        mem = new  int[n+1][d+1];
        s = new int[n+1][d+1];  
        // 默认初始化为0

        int max_value = Completely_backpack(w,v,n,d);
        System.out.printf("背包最大值为:%d\n",max_value);
        System.out.printf("备忘录表为:\n");

        for (int i = 0; i < n + 1; i++) {
            for (int j = 0; j < d + 1; j++) {
                System.out.printf("%d ",mem[i][j]);
            }
            System.out.println();
        }

        System.out.println("标记函数表尾:");
        for (int i = 0; i < n + 1; i++) {
            for (int j = 0; j < d + 1; j++) {
                System.out.printf("%d ",s[i][j]);
            }
            System.out.println();
        }
        // 追踪解 且 初始化为 0
        int []res = new int[n+1];
        traceSolution(res,n,d,w);
        System.out.println("背包装入各个物品的数量为:");
        for (int i = 1; i < n + 1; i++) {
            System.out.printf("%d ",res[i]);
        }
    }

    public static int Completely_backpack(int []w,int []v,int n,int d){
        // F_k(y) = max{F_{k-1}(y), F_k(y-w_k)+v_k }
        // i表示 前i个 物品放入背包
        for (int i = 1; i <= n; i++) {
            // j 表示  背包重量为j
            for (int j = 1; j <= d; j++) {
                int not = mem[i-1][j];
                // w[i-1]是因为 w下标从0 开始,而i从1开始
                int in;
                if (j-w[i-1] < 0){
                    in = Integer.MIN_VALUE;
                }
                else in = mem[i][j-w[i-1]] + v[i-1];

                mem[i][j] = Math.max(not,in);
                // 根据标记函数的定义来写
                if (not > in){
                    s[i][j] = s[i-1][j];
                }
                else{
                    s[i][j] = i;
                }
            }
        }
        return mem[n][d];
    }

    public static void traceSolution(int []res,int n,int d,int []w){
        int y = d;
        for (int i = n; i >0 ;) {
            int temp = s[i][y];
            while(temp == i){
                // i-1 符合w的下标
                y = y-w[i-1];
                res[i]++;
                temp = s[i][y];
            }
            i = s[i][y];
        }
    }
}

你可能感兴趣的:(背包问题(完全背包))