動態規划本質理解:01背包問題


題目描述:01背包問題 w:重量 v:價值 cap:承重


1.遞歸解法:每一個物品都有取和不取兩種決策,所以遞歸的時間復雜度為O(2^n),兩種決策所得到的價值分別為:maxValueRe(w, v, cap, n, curCap + w[index], index + 1) +v[index] 和maxValueRe(w, v, cap, n, curCap, index + 1)),取兩種決策的結果的最大值即為最大價值。

2.備忘錄解法:遞歸有很多重復計算,通過一個二維數組來存儲遞歸的子問題的結果,簡化計算。

3.自底向上(非遞歸寫法):與備忘錄寫法一樣,只是換了一種方式,用循環。

4.滾動數組:優化非遞歸解法,因為當前子問題的結果只與上一個子問題的結果相關聯,由result[i][j] = Math.max(result[i][j], result[i - 1][j- w[i]]+ v[i])可知,i只與i-1有關系,j不變,因此開辟的數組只需要一個只有兩列的二維數組就可以了。

Talk is cheap,show me the code !!!

參考代碼:

package Dp;

import org.junit.Test;

/**
 * 01背包問題 w:重量 v:價值 cap:承重
 * 
 * @author Tongkey
 */
public class Backpack {
    public int[][] result;

    /**
     * 遞歸解法,時間復雜度為O(2^n)
     * 
     * @param w
     *            重量
     * @param v
     *            價值
     * @param cap
     *            承重
     * @param n
     *            數量
     * @param curCap
     *            當前的重量 總重量 = 當前的重量+剩余的重量
     * @param index
     *            當前的下標值
     * @return
     */
    public int maxValueRe(int[] w, int[] v, int cap, int n, int curCap,
            int index) {
        if (curCap > cap) {
            return 0;
        }
        if (index >= n) {
            return 0;
        }
        return Math.max(maxValueRe(w, v, cap, n, curCap + w[index], index + 1)
                + v[index], maxValueRe(w, v, cap, n, curCap, index + 1));
    }

    /**
     * 備忘錄解法(自頂向下),時間復雜度O(n*cap),空間復雜度O(n*cap)
     * 
     * @param w
     *            重量
     * @param v
     *            價值
     * @param cap
     *            承重
     * @param n
     *            數量
     * @param curCap
     *            當前的重量 總重量 = 當前的重量+剩余的重量
     * @param index
     *            當前的下標值
     * @return
     */
    public int maxValueMemory(int[] w, int[] v, int cap, int n, int curCap,
            int index) {
        if (curCap > cap) {
            return 0;
        }
        if (index >= n) {
            return 0;
        }
        if (result[index][curCap] > 0) {
            return result[index][curCap];
        }
        result[index][curCap] = Math
                .max(maxValueRe(w, v, cap, n, curCap + w[index], index + 1)
                        + v[index], maxValueRe(w, v, cap, n, curCap, index + 1));
        return result[index][curCap];
    }

    @Test
    public void testMaxValueMemory(){
        int[] w = { 42, 25, 30, 35, 42, 21, 26, 28 };
        int[] v = { 261, 247, 419, 133, 391, 456, 374, 591 };
        int n = 8;
        int cap = 297;
        result = new int[n][cap + 1];
        int maxValueRe = maxValueMemory(w, v, cap, n, 0, 0);
        System.out.println(maxValueRe);
        System.out.println("----------------------");
    }
    
    /**
     * 自底向上(非遞歸寫法),時間復雜度O(n*cap),空間復雜度O(n*cap)
     * 
     * @param w
     * @param v
     * @param cap
     * @param n
     * @param curCap
     * @param index
     * @return
     */
    public int maxValueDp(int[] w, int[] v, int cap, int n, int curCap,
            int index) {

        result[0][0] = 0;
        // 第一行
        for (int i = 1; i <= cap; i++) {
            if (i >= w[0])
                result[0][i] = v[0];
        }
        // 第一列
        for (int i = 1; i < n; i++) {
            result[i][0] = 0;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= cap; j++) {
                // 默認值,不取index當前的重量值
                result[i][j] = result[i - 1][j];
                if (j >= w[i])
                    result[i][j] = Math.max(result[i][j], result[i - 1][j
                            - w[i]]
                            + v[i]);
            }
        }
        return result[n - 1][cap];
    }

    /**
     * 自底向上(利用滾動數組非遞歸寫法優化),時間復雜度O(n*cap),空間復雜度O(cap)
     * @param w
     * @param v
     * @param cap
     * @param n
     * @param curCap
     * @param index
     * @return
     */
    public int maxValueDpMod(int[] w, int[] v, int cap, int n, int curCap,
            int index) {

        result[0][0] = 0;
        // 第一行
        for (int i = 1; i <= cap; i++) {
            if (i >= w[0])
                result[0][i] = v[0];
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= cap; j++) {
                result[i % 2][j] = result[(i - 1) % 2][j];
                if (j >= w[i])
                    result[i % 2][j] = Math.max(result[i % 2][j],
                            result[(i - 1) % 2][j - w[i]] + v[i]);
            }
        }
        return Math.max(result[0][cap], result[1][cap]);
    }

    /**
     * 把二維數組優化為一維數組版本
     * @param w
     * @param v
     * @param n
     * @param cap
     * @return
     */
    public int maxValueDp2(int[] w, int[] v, int n, int cap) {
        // 給定物品的重量w價值v及物品數n和承重cap
        int[] d = new int[cap + 1];
        for (int i = 0; i < n; i++) {
            for (int j = cap; j >= w[i]; j--) {
                d[j] = Math.max(d[j], d[j - w[i]] + v[i]);
            }
        }
        return d[cap];
    }

    @Test
    public void testMaxValueRe() {
        int[] w = { 42, 25, 30, 35, 42, 21, 26, 28 };
        int[] v = { 261, 247, 419, 133, 391, 456, 374, 591 };
        int n = 8;
        int cap = 297;
        result = new int[2][cap + 1];
        int maxValueRe = maxValueDpMod(w, v, cap, n, 0, 0);
        System.out.println(maxValueRe);
        System.out.println("----------------------");
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM