An algorithm weekly question 004: Backpack

problem:

Suppose you go camping. Do you have a capacity of 6-pound backpack, need to decide which of the following things to carry. Where everything has a corresponding value, the greater the value the more important meaning:
• water (three pounds, the value of 10);
• Books (1 pound, worth 3);
• Food (2 lb, $ 900) ;
• jackets (2 lb, worth 5);
• camera (1 pound, $ 600).
When I ask what things carry the highest value?

Ideas:

This is a classic knapsack problem, to solve such problems, often using dynamic programming. Do not be scared of the name, it is not difficult. Let's explain the algorithm.

Dynamic programming algorithm is started from the grid, we first draw a grid as follows:
An algorithm weekly question 004: Backpack
Each row of the grid represents an item, each column represents a backpack different capacities, from here 1-6. Items marked in brackets behind the weight and value of the items.

Start solving the following.
We need to from left to right, top to bottom to fill each cell.
The first cell, where 1-pound backpack, the first items to be loaded and now water, we found that the weight of water is 3 lbs, not loaded into this case, the cell 0 is filled, that they can not load any article.
The second cell is the same.
An algorithm weekly question 004: Backpack

When the third cell, the capacity of the backpack is three pounds of weight of water is three pounds just can hold, at this time, the cell is filled, the fill value of water. When the fourth cell, backpack capacity is 4 lbs, not only can hold water, and 1 pound margin, but we currently only one item of water, so the remaining space can not, therefore, the backpack can hold a maximum value or three pounds of water, the value is 10. The fifth, sixth cells as well.
An algorithm weekly question 004: Backpack

At this point, 6 lbs backpack can carry a maximum value of 10 items. Next, we are ready to load the second items.
The second item is a book, the weight is 1 pound capacity is just to put the backpack 1 pounds.An algorithm weekly question 004: Backpack

接着是2磅容量的背包,也能放下书。但当背包容量到3时,有情况发生了。
此时我们需要考虑一下,到底是放1磅重的书呢,还是3磅重的水,显然,水的价值更大。所以10 vs 3的结果明显是10胜出,所以第二行第三个单元格的最大价值是10。
An algorithm weekly question 004: Backpack

第四个单元格的情况又有变化。我们此时的背包容量是4磅,书的重量是1磅,如果装了书,那么余下的容量是3磅,我们看一下前一行,背包容量是3磅时的最大价值为10。这就意味着,这一个单元格可以同时放下书和水,共计4磅重量,价值为3+10=13。我们跟前一行的第四单元格对比一下,之前的最大价值是10。现在13 vs 10,明显13胜出,所以,第二行第四单元格我们的最大价值是13。
An algorithm weekly question 004: Backpack

以此类推,第二行第五,第六单元格的价值也可以算出来。
An algorithm weekly question 004: Backpack

接着我们开始算第三行。
第三行第一单元格是放不下食物的,所以沿用前一行对应单元格的价值3。
第二单元格时,有两种选择,一是沿用前一行的物品书,二是放入食物,对比一下价值,很明显应该放入价值9的食物。
An algorithm weekly question 004: Backpack

第三至六单元格我们用之前第二行的算法进行计算,填充如下:
An algorithm weekly question 004: Backpack

第四行填充:
An algorithm weekly question 004: Backpack

第五行填充:
An algorithm weekly question 004: Backpack

当我们填充完后,最后一行最后一个单元格中的价值便是我们能达到的最大价值。

比如最后一行,最后一个单元格。我们的填充方法是:
先取到前一行相同容量背包的价值,这里是22;
当前物品的价值是6,重量是1,如果将该物品放入背包中,则余下容量是5。
前一行背包容量为5时的最大价值是19,即之前5磅背包最多可以放下价值19的物品,再加上当前物品的价值6,合计能放下价值19+6=25的物品;
22 vs 25,取最大的值,则最终能放下价值25的物品。

我们可以归纳出算法:
An algorithm weekly question 004: Backpack

这便是网上流传的公式:
An algorithm weekly question 004: Backpack

解答:

有了算法,我们便可以用代码来实现:

这里我们用php来实现,首先我们把问题先整理出来,如下:

$goods = array(
    array("name" => "水", "weight" => 3, "value" => 10),
    array("name" => "书", "weight" => 1, "value" => 3),
    array("name" => "食物", "weight" => 2, "value" => 9),
    array("name" => "夹克", "weight" => 2, "value" => 5),
    array("name" => "相机", "weight" => 1, "value" => 6),
);
$maxWeight = 6; //背包最大容量

Then, begin to write algorithms:

$table = array();// 表格
foreach ($goods as $i => $good) {
    for ($j = 1; $j <= $maxWeight; $j++) {
        // 填充第一行
        if ($i == 0) {
            if ($j < $good['weight']) {
                $table[$i][$j] = 0;
            } else {
                $table[$i][$j] = $good['value'];
            }
        } else {
            $v1 = $table[$i - 1][$j];
            if ($j < $good['weight']) { // 当装不下时,以前一格为准
                $table[$i][$j] = $v1;
            } else {
                // 1.前一行同列的值;2.当前物品价值+余下重量的最大价值。这两者取最大值
                if ($j == $good['weight']) {
                    $preMax = 0;
                } else {
                    $preMax = $table[$i - 1][$j - $good['weight']];
                }
                $v2 = $good['value'] + $preMax;
                $table[$i][$j] = max($v1, $v2);
            }
        }
    }
}
print_r($table);

We print the form, the following results, you can compare the results of the hand count:

Array
(
    [0] => Array
        (
            [1] => 0
            [2] => 0
            [3] => 10
            [4] => 10
            [5] => 10
            [6] => 10
        )

    [1] => Array
        (
            [1] => 3
            [2] => 3
            [3] => 10
            [4] => 13
            [5] => 13
            [6] => 13
        )

    [2] => Array
        (
            [1] => 3
            [2] => 9
            [3] => 12
            [4] => 13
            [5] => 19
            [6] => 22
        )

    [3] => Array
        (
            [1] => 3
            [2] => 9
            [3] => 12
            [4] => 14
            [5] => 19
            [6] => 22
        )

    [4] => Array
        (
            [1] => 6
            [2] => 9
            [3] => 15
            [4] => 18
            [5] => 20
            [6] => 25
        )

)

Here is not over, we have to know which items to install, so it needs a backtracking.
From back to front backstepping, the same if the current cell is the value of the previous line the same column of cells prices, descriptions current article is not added to the backpack, calculated as $ x [$ i] = 0 ;
otherwise, it is calculated as $ x [$ i] = 1, the current and subtracting the weight of the total weight of the article;
when the items back to the first, a look at the value of $ j, if non negative, is the first item described in terms of selected items 1, otherwise the count is 0.

$j = $maxWeight;
$n = count($goods);
$x = array();// 物品数组
for ($i = $n - 1; $i >= 0; $i--) {
    if ($i > 0) {
        if ($table[$i][$j] == $table[$i - 1][$j]) {
            $x[$i] = 0;
        } else {
            $x[$i] = 1;
            $j -= $goods[$i]['weight'];// 每次扣减当前物品的重量
        }
    } else {
        $x[$i] = $j >= 0 ? 1 : 0;// 如果最后发现$j是有值的,那便是第1个物品
    }
}
ksort($x);// 把回溯的过程改为顺序

foreach ($x as $key => $val) {
    if ($val != 0) {
        print_r($goods[$key]);
    }
}

The results are as follows:

Array
(
    [name] => 水
    [weight] => 3
    [value] => 10
)
Array
(
    [name] => 食物
    [weight] => 2
    [value] => 9
)
Array
(
    [name] => 相机
    [weight] => 1
    [value] => 6
)

The following is the realization Golang, the same algorithm, but a change languages ​​to achieve it

package main

import (
    "fmt"
    "math"
)

type Goods struct {
    Name   string
    Weight int
    Value  int
}

var GoodsList []Goods // 物品列表
var maxWeight = 6     // 背包最大容昊

func main() {
    GoodsList = []Goods{
        {Name: "水", Weight: 3, Value: 10},
        {Name: "书", Weight: 1, Value: 3},
        {Name: "食物", Weight: 2, Value: 9},
        {Name: "夹克", Weight: 2, Value: 5},
        {Name: "相机", Weight: 1, Value: 6},
    }

    table := make([][]int, len(GoodsList))
    for i, goods := range GoodsList {
        table[i] = make([]int, maxWeight+1)
        fmt.Println()
        for j := 1; j <= maxWeight; j++ {
            if i == 0 {
                if goods.Weight > j {
                    table[i][j] = 0
                } else {
                    table[i][j] = goods.Value
                }
            } else {
                v1 := table[i-1][j]
                if goods.Weight > j {
                    table[i][j] = v1
                } else {
                    preMax := 0
                    if j == goods.Weight {
                        preMax = 0
                    } else {
                        preMax = table[i-1][j-goods.Weight]
                    }
                    v2 := goods.Value + preMax
                    //fmt.Println(v1, v2, goods)
                    table[i][j] = int(math.Max(float64(v1), float64(v2)))
                }
            }
        }
    }

    fmt.Println(table)
    goodsNum := len(GoodsList)
    j := maxWeight
    x := make([]int, goodsNum)
    for i := goodsNum - 1; i >= 0; i-- {
        if i > 0 {
            if table[i][j] == table[i-1][j] {
                x[i] = 0
            } else {
                x[i] = 1
                j -= GoodsList[i].Weight
            }
        } else {
            if j >= 0 {
                x[i] = 1
            } else {
                x[i] = 0
            }
        }
    }

    for key, value := range x {
        if value == 1 {
            fmt.Println(key, GoodsList[key])
        }
    }
}

Export

[[0 0 0 10 10 10 10] [0 3 3 10 13 13 13] [0 3 9 12 13 19 22] [0 3 9 12 14 19 22] [0 6 9 15 18 20 25]]
0 {水 3 10}
2 {食物 2 9}
4 {相机 1 6}

Guess you like

Origin blog.51cto.com/ustb80/2423138