每天一道算法题(四) (动态规划算法)01背包问题Java实现

动态规划


动态规划在wiki上的定义:

dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions - ideally, using a memory-based data structure. The next time the same subproblem occurs, instead of recomputing its solution, one simply looks up the previously computed solution。


昨天接触到了动态规划的概念,研究了昨天一晚上以及今天一上午,总算对这个问题有些收获。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。
从空集合开始,每增加一个元素就求它的最优解,直到所有元素加进来,就得到了总的最优解。


01背包问题

01背包问题即的01即每件物品最多放1件,否则不放入。
让我真正了解动态规划概念的是mu399的博客

问题:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?


重新定义问题:

  1. 有承重分别为1-10的背包10个
  2. 编号分别为a,b,c,d,e的物品各一个
name weight value
a 2 6
b 2 3
c 6 5
d 5 4
e 4 6

3. 从e物品开始依次放入1-10个背包,分别得到最大的价值总和
4. 把d物品放入依次放入存在e物品的1-10个背包,如果价值更高,替换掉e()
5. c,b,a同理。。。

name weight value 1 2 3 4 5 6 7 8 9 10
e 4 6 0 0 0 6 6 6 6 6 6 6
d 5 4 0 0 0 6 6 6 6 6 10 10
c 6 5 0 0 0 6 6 6 6 6 10 11
b 2 3 0 3 3 6 6 9 9 9 10 11
a 2 6 0 6 6 9 9 12 12 15 15 15

思路:
1. 01背包的状态转换方程 f[i,j] = Max{f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }

f[i,j]:在前i件物品中选择若干件放在承重为 j 的背包中,可以取得的最大价值。
Pi表示第i件物品的价值。
决策:为了背包中物品总价值最大化,第 i件物品应该放入背包中吗 ?
2. 以a8(行为a,列为的8的单元格)举例
f[i,j] = a8 = 15
f[i-1,j] = b8 = 9
f[i-1,j-Wi] 表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值
f[i-1,j-Wi] +Pi =b(8 - 2) + 6 = b6 + 6 = 15

背包的java代码实现

public class getPgAnswer{

    @Test
    public void testPackage() {

        Package[] pg =  {new Package("e",4,6),
                         new Package("d",5,4),
                         new Package("c",6,5),
                         new Package("b",2,3),
                         new Package("a",2,6)
                         };
        // 第一个参数表示从pg[0]开始依次放入的物品,
        // 第二个参数代表背包的承重,放弃第0列数组
        int[][] state = new int[pg.length][11];
        int newValue = 0;

        /**
         *  01背包的状态转换方程
         *  f[i,j] = Max{
         *               f[i-1,j-Wi]+Pi( j >= Wi ), 
         *               f[i-1,j] }
         */
        for (int i = 0; i < pg.length; i++) {
            // 背包的承重量
            for (int j = 1; j < state[i].length; j++) {
                if (i == 0) {
                    if (pg[i].getWeight() <= j) {
                        state[i][j] = pg[i].getValue();
                    }
                }else{
                    state[i][j] = state[i - 1][j];
                    if (j < pg[i].getWeight()) {
                        continue;
                    }
                    newValue = state[i - 1][j - pg[i].getWeight()]
                            + pg[i].getValue();
/*                  if (newValue  >= state[i - 1][j]) {
                        state[i][j] = newValue;
                    }else{
                        state[i][j] = state[i - 1][j];
                    }*/
                    state[i][j] = Math.max(newValue, state[i - 1][j]);
                }
            }
        }
        for (int i = 0; i < state.length; i++) {
            System.out.println(Arrays.toString(state[state.length - 1 - i]));
        }
    }
}

class Package {

    private String name;

    private int weight;

    private int value;

    public Package(String name,int weight,int value){
        this.name = name;
        this.weight = weight;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public int getWeight() {
        return weight;
    }

    public int getValue() {
        return value;
    }
}

背包的Python实现

"""
    问题:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,
         现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

    思路:dynamic programming
        即把整个问题分解成一系列子问题,所有子问题只计算一次并存起来,下一次相同问题出现,直接从数据结构中取出结果,而不是再次计算
        K(i, j) = max(K(i - 1, j), K(i - 1, j - Wi) + Pi)
        其中 j >= Wi
            f[i,j]:在前i件物品中选择若干件放在承重为 j 的背包中,可以取得的最大价值。
            Pi表示第i件物品的价值。
            决策:为了背包中物品总价值最大化,第 i件物品应该放入背包中吗


"""


def package(n, c, weight, values):
    """
    构建背包问题二维表
    :param n: 物品数量
    :param c: 背包重量容积
    :param weight: 物品重量列表
    :param values: 物品价值列表
    :return:
    """
    # 初始化二维列表
    res = [[-1 for j in range(c + 1)] for i in range(n + 1)]
    res[0] = [0 for i in range(1, c + 1)]


    # 完善背包
    for i in range(1, n + 1):
        for j in range(1, c + 1):
            if i == 1:
                res[1][j] = values[1] if j >= weights[1] else 0
            else:
                res[i][j] = res[i - 1][j]
                if weights[i] < j and res[i - 1][j - weights[i]] + values[i] > res[i][j]:
                    res[i][j] = res[i - 1][j - weights[i]] + values[i]
                # @Note:如果没有上面的res[0] = [0 for i in range(1, c + 1)] 需要增加下面的判定
                # elif weights[i] == j and values[i] > res[i][j]:
                #     res[i][j] = values[i]
    return res


if __name__ == '__main__':
    # 物品数量
    n = 5
    # 背包重量容积
    c = 10
    # 物品重量列表 weights[0] 无效 和blog有区别 博客是倒着放入商品
    weights = [-1, 2, 2, 6, 5, 4]
    # 物品价值列表 values[0] 无效
    values = [-1, 6, 3, 5, 4, 6]
    res = package(n, c, weights, values)

你可能感兴趣的:(每天一道算法题,Python)