动态规划算法解决背包问题

什么是背包问题?

  • 问题描述:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。
  • 如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题。
  • 如果限定物品最多只能选择j个(j = 0, 1, 2…),则问题称为有界背包问题。
  • 如果不限定每种物品的数量,则问题称为无界背包问题。

使用动态规划解决0-1背包问题:
假如我们有以下三个物品,需要放入容量为4的背包里,使得总价格最大:

在这里插入图片描述
我们先使用一个数组items[ ]储存物品名字,数组w[ ]储存物品的重量,数组v[ ]储存物品的价格。然后根据物品个数itemsNum以及背包容量capacity定义一个二维数组:

String[] items = {"Guitar", "Speaker", "Computer"};
int[] w = {1, 4, 3};
int[] v = {1500, 3000, 2000};
int[][] table = new int[itemsNum + 1][capacity + 1];

其中table[ i ][ j ] 表示在前i个物品中能够装入容量为j的背包中的最大价格。
当i = 0时,物品为0,则最大价格也为0,所以将table[ i ][ 0 ]设置为0;
当j = 0时,背包容量为0,则最大价格也为0,所以将table[ 0 ][ j ]设置为0;

从i = 1开始遍历,求出背包容量从1到规定容量的最大价格,步骤如下:

  1. 当w[ i - 1 ] > j,即当前物品的重量超过了当前背包容量时,就直接使用上一个单元格的装入策略:
    table[ i ][ j ] = table[ i - 1 ][ j ]。
  2. 当w[ i - 1 ] <= j 时, table[ i ][ j ] = max( table[ i - 1 ][ j ],v[ i - 1] + table[ i - 1 ][ j - w[ i - 1 ] ])。
    其中table[ i - 1 ][ j ]表示上一个单元格的装入的最大值;
    v[ i - 1]表示当前物品的价格;
    table[ i - 1 ][ j - w[ i - 1 ] ]表示将i - 1个商品装入到容量为 j - w[ i - 1 ] 的背包中所能达到的最大价格。

遍历结束后,二维数组table的结果应如下图所示:

在这里插入图片描述
代码实现:

package DataStructure;


public class DynamicProgramDemo {
    public static void main(String[] args) {
        String[] items = {"Guitar", "Speaker", "Computer"};
        int[] weight = {1, 4, 3};
        int[] value = {1500, 3000, 2000};
        Knapsack knapsack = new Knapsack(7, items, weight, value);
        knapsack.theMaxValue();
        /* output:
        将Computer放入背包
        将Speaker放入背包
         */
    }
}

class Knapsack {
    public int itemsNum; // 物品数量
    public int capacity; // 背包容量
    public String[] items; // 物品数组
    int[] w; // 物品重量数组
    int[] v; // 物品价值数组

    public Knapsack() {

    }

    public Knapsack(int capacity, String[] items, int[] weight, int[] value) {
        this.capacity = capacity;
        this.items = items;
        this.itemsNum = items.length;
        this.w = weight;
        this.v = value;
    }

    public void theMaxValue() {
        // table二维数组,用于记录不同物品数量不同背包容量时的最大价值
        int[][] table = new int[itemsNum + 1][capacity + 1];
        // updateItem布尔类型二维数组,用于记录当背包容量不变,而物品数量增多时,最大价值是否更新
        boolean[][] updateItem = new boolean[itemsNum + 1][capacity + 1];

        for (int i = 1; i <= itemsNum; i++) {
            for (int j = 1; j <= capacity; j++) {
                // 如果新增物品的重量大于背包容量,使用新增之前的最大价值
                if (w[i-1] > j) {
                    table[i][j] = table[i-1][j];
                }else { // 否则,将新增后的最大价值与之前的最大价值进行比较,选其中大的值
                    // 注意到:新增后的最大值=新增物品的价值+之前的物品能够装入剩下的背包容量的最大价值
                    if (table[i-1][j] > v[i-1] + table[i-1][j - w[i-1]]) {
                        table[i][j] = table[i-1][j];
                    }else {
                        table[i][j] = v[i-1] + table[i-1][j - w[i-1]];
                        // 最大价值更新后,需要将updateItem的相应位置设置为true
                        updateItem[i][j] = true;
                    }
                }
            }
        }

        // 从后往前遍历,先定位到背包容量为capacity时的最大值处:
        // 在j=capacity列从下往上(i--)找到updateItem值为true时,说明往背包加入物品items[i-1]后,
        // 价值达到了最大,以后无论新增了多少可放入背包的物品,最大值都不再变化,背包里的物品已固定不变。
        // 而又因为最大值 = v[i-1] + table[i-1][j - w[i-1]]
        // 所以需要在j = j - w[i-1]列,查找达到最大值时所添加的物品
        // 如此循环,直到 j <= 0 或 i <= 0
        int j = capacity;
        int i = itemsNum;
        while (j > 0 && i > 0) {
            if (updateItem[i][j]) {
                System.out.println("将"+items[i-1]+"放入背包");
                j -= w[i - 1];
            }
            i--;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值