动态规划学习 0-1背包问题,学习笔记

**

动态规划学习 0-1背包问题,学习笔记

**

  • 最近在学习动态规划,对于一个问题解决的大致思路如下:根据已知问题分析出其状态转移方程 然后根据该方程先使用递归的暴力解法来解解决问题,由于该方式会产生大量的重叠子问题,于是在递归的基础上衍生出了一种记忆化搜索的方式,该方式有效地减少了递归函数的调用。

  • 以上两种方式都是由一种自顶向下的方式进行思考,而动态规划问题则反其道而行之,通过自底向上的方法来解决问题。下面以动态规划的经典问题——0-1背包问题对三种方法进行实现

  • 在我看来 解决问题的关键就是根据实际问题分析出状态转移方程。

下面给出问题描述:
有一个背包,它的容量为C(Capacity),现有n种不同的物品,编号为0……n-1,其中每一件物品的重量为w(i),价值为v(i)。问可以向这个背包中盛放哪些物品,使得在不超过背包容量的基础上,物品的总价值最大。

如果使用暴力解法,则每一件物品都可以放进背包也可以不放进背包,其时间复杂度为O(n * 2^n)

假设方程 F(n,C)代表将n个物品放进容量为C的背包,使其容量最大
则 对于任意的 F(i, C) 都有

  • 若不把编号为i的物品放入背包中,即不考虑该物品,则F(i, C) = F(i - 1, C)
  • 若考虑编号为i的该物品,则F(i, C) = v(i) + F(i - 1, C - w(i))

因此,F(i, C)应为两种情况的最大值,即 F(i, C) = max(F(i - 1, C),v(i) + F(i - 1, C - w(i)) )
从而得到了该状态转移方程。

根据该方程 首先 有递归的暴力解法:

public class Knapsack01 { // 0-1 背包问题  暴力解法
    public int kanpsack01 (int[] w, int[] v, int C) { // 自顶向下

        if (w.length != v.length)
            throw new IllegalArgumentException("Invalid w or v");
            
        int n = w.length;

        if (n == 0 || C == 0)
            return 0;

        memo = new int[n][C + 1];

        for (int i = 0; i < n; i ++)
            for (int j = 0; j < C + 1; j ++)
                memo[i][j] = -1;


        return bestValue(w, v, n - 1, C);
    }

    private int bestValue (int[] w, int[] v, int index, int c) {
        if (index < 0 || c <= 0)
            return 0;
        int res = bestValue(w, v, index - 1, c);
        if (c >= w[index])
            res = Math.max(res, v[index] + bestValue(w, v, index - 1, c - w[index]));
        return res;
    }
}

记忆化搜索的方式 ,可以有效减少递归的次数

public class Knapsack01 { // 0-1 背包问题

    private int[][] memo; //由于该问题被产品序号和背包容量两个因素所约束,故使用二维数组来标记
    public int kanpsack01 (int[] w, int[] v, int C) { // 记忆化搜索方式 自顶向下

        if ( w.length != v.length)
            throw new IllegalArgumentException("Invalid w or v");
            
        int n = w.length;

        if (n == 0 || C == 0)
            return 0;

        memo = new int[n][C + 1];

        for (int i = 0; i < n; i ++)
            for (int j = 0; j < C + 1; j ++)
                memo[i][j] = -1;


        return bestValue(w, v, n - 1, C);
    }

    private int bestValue (int[] w, int[] v, int index, int c) {
        if (index < 0 || c <= 0)
            return 0;
        if (memo[index][c] != -1)
            return memo[index][c];

        int res = bestValue(w, v, index - 1, c);
        if (c >= w[index])
            res = Math.max(res, v[index] + bestValue(w, v, index - 1, c - w[index]));
        memo[index][c] = res;
        return res;
    }
}

最后,反其道而行之,由自底向上的方式去思考 即动态规划

public class Knapsack01 { // 0-1 背包问题

    private int[][] memo;

    public int kanpsack01_Dp (int[] w, int[] v, int C) { //动态规划方式 自底向上
        if (w.length != v.length)
            throw new IllegalArgumentException("Invalid w or v");
            
        int n = w.length;
        memo = new int[n][C + 1];
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < C + 1; j ++)
                memo[i][j] = -1;
        }

        for (int j = 0; j <= C; j ++)
            memo[0][j] = (j >= v[0] ? v[0] : 0 );

        for (int i = 0; i < n; i ++) {
            for (int j = 0; j <= C; j ++) {
                //考虑0-i这些物品 且容积为j的背包所能容的最大价值
                memo[i][j] = memo[i - 1][j];
                if (j >= w[i])
                    memo[i][j] = Math.max(memo[i][j], v[i] + memo[i - 1][j - w[i]]);
            }
        }

        return memo[n -1][C];
    }
}

你可能感兴趣的:(学习笔记,动态规划,背包问题)