1. 程式人生 > >DP揹包之01揹包、完全揹包、多重揹包筆記

DP揹包之01揹包、完全揹包、多重揹包筆記

這是個經典話題,值得好好研究一番,本文作為學習筆記將會不斷更新。

主要參考了以下資料:

受益匪淺!

以下是Java的實現:

package DP;

import java.util.Arrays;

public class Knapsack01 {

	static int N = 3;	// 物品個數
	static int V = 5;	//揹包最大容量  
	static int[] weight = {0,3,2,2};	//物品重量  
	static int[] value = {0,5,10,20}; //物品價值
	static int[][] dp1 = new int[N+1][V+1];
	
	public static void main(String[] args) {
		System.out.println(knapsack01_2D());
		System.out.println(knapsack01_1D());
		System.out.println(completeKnapsack());
		System.out.println(multiKnapsack());
	}
	
	/* 
	01揹包
	目標:在不超過揹包容量的情況下,最多能獲得多少價值 
	 
	子問題狀態:f[i][j]:表示前i件物品放入容量為j的揹包得到的最大價值 
	 
	狀態轉移方程:f[i][j] = max{f[i - 1][j],f[i - 1][j - weight[i]] + value[i]} 
	 
	初始化:f陣列全設定為0 
	*/  
	public static int knapsack01_2D(){
		//遞推  
		for(int i=1; i<=N; i++){		 //列舉物品 
			for(int j=0; j<=V; j++){		//列舉揹包容量
				dp1[i][j] = dp1[i-1][j];
				if(j >= weight[i]){
					dp1[i][j] = Math.max(dp1[i-1][j], dp1[i-1][j-weight[i]]+value[i]);
				}
			}
		}
		return dp1[N][V];
	}
	
	
	//===============================
	static int[] dp2 = new int[V+1];
	/* 
	目標:在不超過揹包容量的情況下,最多能獲得多少價值 
	 
	子問題狀態:f[j]:表示前i件物品放入容量為j的揹包得到的最大價值 
	 
	狀態轉移方程:f[j] = max{f[j],f[j - weight[i]] + value[i]} 
	 
	初始化:f陣列全設定為0 
	*/  
	public static int knapsack01_1D(){
		for(int i=1; i<=N; i++){			//列舉物品    
			for(int j=V; j>=weight[i]; j--){	//注意要逆序列舉容量!!! 防越界,j下限為 weight[i]
				dp2[j] = Math.max(dp2[j], dp2[j-weight[i]]+value[i]);
			}
			System.out.println(Arrays.toString(dp2));
		}
		return dp2[V];
	}

	
	
	/* 
	完全揹包
	f[i][v]:前i件物品放入揹包容量為v的揹包獲得的最大收益 
	 
	f[i][v] = max(f[i - 1][v],f[i - 1][v - k * Wi] + k * Vi,其中 1<=k<= v/Wi) 
	 
	邊界條件 
	f[0][v] = 0; 
	f[i][0] = 0; 
	
	總的複雜度為O(NV*Σ(V/c[i]))
	*/ 
	static int[][] dp3 = new int[N+1][V+1];
	public static int completeKnapsack(){
		
		for(int i=0; i<=N; i++){
			dp3[i][0] = 0;
		}
		for(int v=0; v<=V; v++){
			dp3[0][v] = 0;
		}
		for(int i=1; i<=N; i++){
			for(int v=1; v<=V; v++){
				dp3[i][v] = 0;
				int nCount = v / weight[i];
				for(int k=0; k<=nCount; k++){
					dp3[i][v] = Math.max(dp3[i][v], dp3[i-1][v-k*weight[i]]+k*value[i]);
				}
			}
		}
		return dp3[N][V];
	}
	
	
	//=====================================
	/* 
	多重揹包
	f[i][v]:表示把前i件物品放入容量為v的揹包中獲得的最大收益。 
	f[i][v] = max(f[i - 1][v],f[i - 1][v - k * Weight[i]] + K * Value[i]);
	其中1 <= k <= min(Num[i],V/Weight[i]) 注意這裡受到Num[i]的約束  
	//初始化 
	f[i][0] = 0; 
	f[0][v] = 0; 
	*/
	static int[] num = {0,10,5,2};	//物品數量
	static int[][] dp4 = new int[N+1][V+1];
	public static int multiKnapsack(){
		int nCount = 0;
		for(int i=0; i<=N; i++){
			dp4[i][0] = 0;
		}
		for(int v=0; v<=V; v++){
			dp4[0][v] = 0;
		}
		
		for(int i=1; i<=N; i++){
			for(int v=1; v<=V; v++){
				dp4[i][v] = 0;
				nCount = Math.min(num[i], v/weight[i]);
				for(int k=0; k<=nCount; k++){
					dp4[i][v] = Math.max(dp4[i][4], dp4[i-1][v-k*weight[i]]+k*value[i]);
				}
			}
		}
		return dp4[N][V];
	}
}