算法和数据结构

背包问题(完全背包)

2019-04-28  本文已影响15人  Mereder

动态规划合集:

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

例题3——背包问题

描述

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

组合优化问题,设x_j表示装入背包的第j个物品的数量,解可以表示为<x_1,x_2,\cdots,x_n>。那么目标函数和约束条件是:
目标函数:max\sum_{j=1}^{n}v_jx_j \\ 约束条件:\begin{cases} \sum_{j=1}^{n}w_jx_j \le b \\ x_j \in N \end{cases}
如果组合优化问题的目标函数和约束条件都是线性函数,称为线性规划。如果线性规划问题的变量x_j都是非负整数,则称为整数规划问题。背包问题就是整数规划问题。限制所有的x_j=0 \ or \ x_j=1时称为0-1背包

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

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

y:表示背包总重

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

F_k(y):装前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}
F_k(y)包含两种情况:不装第k种物品或至少装一件第k种物品。

F_k(y-w_k)+v_k的解释:装一件第k种物品后,最优的解法仍然是在前k个物品进行选择,仍有可能再选入1件第k种物品。

对边界条件:

F_1(y) = \lfloor\frac{y}{w_1}\rfloor v_1:即只用第一种物品背包重量限制为y的最大价值,为了保证背包不超重,第一种物品至多能装\lfloor\frac{y}{w_1}\rfloor个,因为背包价值为\lfloor\frac{y}{w_1}\rfloor v_1

F_k(y) = - \infty,\quad y<0 有些F_k(y-w_k)<0那么通过设置为负无穷,在选择过程中抛弃掉为负的情况。

标记函数:用来追踪解
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.

实例

n=4,b=10 \\ v_1=1,v_2=3,v_3=5,v_5=9 \\ w_1=2,w_2=3,w_3=4,w_4=7 \\

{F_{k}(y)=\max \left\{F_{k-1}(y), F_{k}\left(y-w_{k}\right)+v_{k}\right\}}

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

{F_2(1)=\max \{F_{2-1}(1), F_{2}(1-w_{2})+v_{2}\}} = max\{0,-\infty\} = 0

{F_2(2)=\max \{F_{2-1}(2), F_{2}(2-w_{2})+v_{2}\}} = max\{1,-\infty\} = 1

{F_2(3)=\max \{F_{2-1}(3), F_{2}(3-w_{2})+v_{2}\}} = max\{1,3 \} = 3

{F_2(4)=\max \{F_{2-1}(4), F_{2}(4-w_{2})+v_{2}\}} = max\{2,3 \} = 3

{F_2(5)=\max \{F_{2-1}(5), F_{2}(5-w_{2})+v_{2}\}} = max\{2,1+3 \} = 4

{F_2(6)=\max \{F_{2-1}(6), F_{2}(6-w_{2})+v_{2}\}} = max\{2,3+3 \} = 6

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

备忘录表

标记函数的备忘录:

标记函数表

关于背包问题的总结

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

0-1背包问题x_i = 0\ or\ 1,i=1,2,\cdots,n

多背包:m个背包,背包j装入最大重量B_j,j=1,2,\cdots,m在满足所有背包重量约束下使物品价值最大。

二维背包:每件物品重量w_i和体积t_j,i=1,2,\cdots,n,背包总重不超过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];
        }
    }
}

上一篇 下一篇

猜你喜欢

热点阅读