整數0-1揹包問題

-Billy發表於2018-08-08

• 小偷有一個容量為W的揹包,有n件物品,第i個物品價值vi,且重wi。
• 目標: 找到xi使得對於所有的xi = {0, 1},sum(wi*xi) <= W, 並且 sum(xi*vi)最大。

常規思路

public class BackPack
{
    static int [] w = new int[] {3, 4, 5, 7, 6}; // 每件物品的重量
	static int[] v = new int[] {4, 5, 2, 8, 9}; // 每件物品的價值 
	static int N = w.length; // 物品的個數
	
	final int W = 15; // 揹包的總重量
	/**
	 * @param idx 當前遍歷的是第幾個物品
	 * @param S 當前物品的總重量
	 * @return 最大的價值
	 */
	int search(int idx, int S)
	{
		// 當前物品的總重量和大於揹包的重量
		if (S > W)
		{
			return 0;
		}
		// 遍歷到最後一個物品了
		if (idx >= N)
		{
			return 0;
		}
		return Math.max(search(idx+1, S + w[idx]) + v[idx], search(idx+1, S));
	}
}

 程式碼裡冗餘太多(有多種方法都可以使得當前物品的總重量達到S,每一種構成方式都會去算一遍),對於

當前的狀態而言,我們只關心的使用的多少,如何使用的並不關心。

改進後:

	int[][] f = new int[N][W]; // 定義f(i,j):當前揹包容量 j,前 i 個物品最佳組合對應的價值;

         // 初始化塊
	{
		for (int i=0; i<N; i++)
		{
			for (int j=0; j<W; j++)
			{
				f[i][j] = -1;
			}
		}
	}
	
	int search(int idx, int S)
	{
		// 當前物品的總重量和大於揹包的重量
		if (S > W)
		{
			return 0;
		}
		// 遍歷到最後一個物品了
		if (idx >= N)
		{
			return 0;
		}
		// 如果計算過
		if (f[idx][S] >= 0) { // F(i, W)表示前i件物品體積為w,最大價值  
			return f[idx][S];
		}
		f[idx][S] =  Math.max(search2(idx+1, S + w[idx]) + v[idx], search2(idx+1, S));
		return f[idx][S];
	}

迭代法:自底向上求解

	int search()
	{
		dp[0][0] = 0; 
		// 賦初始值
		for (int i=0; i<=W; ++i)
		{
			dp[0][i] = (int) Double.NEGATIVE_INFINITY; // 表示負無窮   
		}
		
		for (int i=1; i<=N; ++i)
		{
			dp[i][0] = 0; // (先要把已知的條件填好)
			for (int j=1; j<=W; ++j) // 揹包的容量的範圍
			{
				dp[i][j] = dp[i-1][j]; // 表示還沒選第idx件物品
				if (j >= w[i]) // 表示揹包中能放進去
					// 注意這裡在計算的時候要利用之前已經計算出來的狀態
					dp[i][j] =  Math.max(dp[i-1][j - w[i]] + v[i], dp[i-1][j]);
			}
		}
		return dp[N][W];
	}

 

相關文章