从算法看背包问题(1)

从算法看背包问题(1)

背包问题(Knapsack problem)是一种组合优化的NP彻底问题。问题能够描述为:给定一组物品,每种物品都有本身的重量和价格,在限定的总重量内,咱们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。类似问题常常出如今商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。javascript

N件物品和一个容量为C的背包。第i件物品的重量(即体积,下同)是W[i],价值是V[i]。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,且价值总和最大。java

简化一下吧,一个最大重量为5的背包,有以下物件:算法

物品 重量 价值
0 2 3
1 3 4
2 4 5

请问应如何选取,能使背包价值最大?后端

建表

在作这题以前,应该创建一个表。优化

把这个表填满,就是用程序去实现算法的过程。this


先决条件

令对应格子的可以存放的最大价值为$f(i,j)$,code

第一条重要原则,是解决问题的先决条件blog

空间能给你放,你就放。

转化为数学语言就是:继承

第一行分析

先来看第一行,只考虑物品1:ip

  • 考虑容量C[j],j为0,1时,物品0毫无疑问放不下。

    ∴ f(2,j)=0;

  • C为5时,能够放下。因此填充的价值为3.f(0,2)=v[0]=2.

  • ...

第一行,要么放不下(为0,j<w[i]),要么放进去(v[i],此时j>=w[i]).

由此:

此时填充的表格为:

第二行分析

接着看第二行。

  • 考虑容量C[j],j为0,1,2时,物品1毫无疑问放不下。f(1,j)继承f(0,j)的值。

  • j为3时,由于w[0]+w[1]>j,所以只存在个选择:要么放物品0,要么放物品1.放和不放要经过比较来决定。

    若是不放物品0,那么这个值为4

    若是决定物品0,那么容量此时变成了j-w[0],那么去找f(1,j-w[0])这个格子。显然f(1,j-w[0])==f(1,3)==0,咱们要取大的那个,因此只放物品1为最优解。填入4。

  • j=5时。两个都放得下。因此f(1,5)=7

经过比较得知:此时填充的表格为:

好了,背包问题的算法实际上能够结束了。

多余的第三行分析:概括现象

为了作完整,最后再看第三行:

  • j=0,1,2,3(j<w[2])时,根本放不下物品2,不予考虑,此时:f(2,j)继承f(1,j)的值。

    $f(2,j)=f(1,j)$。

  • 当j=4也就是w[2]<=j时,能够放下物品2,此时须要比较

    先看f(1,4),它占用了w[1]=3的空间,此时空间为j-3,找到f(2,j-3)==f(2,1)==0

    若是直接放物品2.结果为5,所以填上5。

  • 最后看C5。仍是比较:f(1,5)的方案是放物品0和1,占用为2+3=5,此时空间还剩下5-5=0,去检索f(2,0)得0.

    所以,取最大填上7。

算到最后,你会发现,问题的解答在于表格右下角,也就是所有的最大值。也就是7.

同时你也会发现第二第三行实际上是同样的。

所以

算法实现

假如后端给你的数据以下:

let table = [{
    good: '鸡蛋',
    weight: 2,
    value: 3
}, {
    good: '西红柿',
    weight: 3,
    value: 4
}, {
    good: '茄子',
    weight: 4,
    value: 5
}]

还须要处理下。

class Knapsack {
    constructor(table, capacity) {
        // 初始化
        this.nums = table.length - 1;
        this.goods = [];
        this.weights = [];
        this.values = [];
        this.capacity = capacity;
        table.map((x, i) => {
            this.goods.push(x.good);
            this.weights.push(x.weight);
            this.values.push(x.value);
        });

        /**
         * 封装一个类,放到表格中
         * items为名目
         *  */
        this.UnitData = function () {
            this.init = function (items, value) {
                return {
                    items, value
                };
            }
        }
    }

    getItems() {
        // 初始化表格
        let table = [[]];
        let { UnitData, capacity, nums, weights, values, goods } = this;

        // 建立列,第一行判断
        for (let j = 0; j <= capacity; j++) {
            let unitData = new UnitData();
            if (j < this.weights[0]) {
                // 啥也放不了
                table[0][j] = unitData.init([], 0)
            } else {
                // 容许放第一个商品
                table[0][j] = unitData.init([goods[0]], values[0])
            }
        }

        // 第二行开始判断
        for (let j = 0; j <= capacity; j++) {
            for (let i = 1; i <= nums; i++) {
                // 建立行
                if (!table[i]) {
                    table[i] = [];
                }
                // 第2个商品起开始比较判断
                if (j < weights[i]) {
                    // 容量小则继承
                    table[i][j] = table[i - 1][j];
                } else {
                    //不然比较,查找。
                    let a = table[i - 1][j].value;
                    let b = table[i - 1][j - weights[i]].value + values[i]

                    if (a > b) {
                        table[i][j] = table[i - 1][j];
                    } else {
                        let unitData = new UnitData();
                        // 终于找到了。把物品扔进去!妈了个巴子的
                        table[i - 1][j].items.push(goods[i]);
                        table[i][j] = unitData.init(table[i - 1][j].items, table[i - 1][j - weights[i]].value + values[i])
                    }
                }
            }
        }

        // 返回表格右下角的数据
        return table[nums][capacity]
    }


}

var a = new Knapsack(table, 5)

console.log(a.getItems())
// { items: [ '鸡蛋', '西红柿' ], value: 7 }

由此,基于动态规划算法的0-1背包问题的问题解决。

相关文章
相关标签/搜索