1. 程式人生 > >0-1揹包問題

0-1揹包問題

核心:問題分解、狀態轉移

1問題及求解

一堆物品,編號1,2,…,n,重量w1,w2,…,wn,價值v1,v2,…,vn,現在有一個包,能承受的最大重量為w,問選裝哪幾件物品,能帶走的價值最大?

核心:問題分解、狀態轉移

分解:對每個物品就兩種情況,要麼裝,要麼不裝,那麼我們從最後一個物品n開始逐個考察。

狀態轉移:對第i個物品,當前揹包容量為j,當前揹包內最大價值為val[i][j],

如果它的重量比當前揹包容量大,放棄,看下一個,即 val[i][j]=val[i-1][j];

如果它的重量不大於當前揹包容量,那裝還是不裝,按總價值大的來,

    i)不裝的話,最大總價值和前面一樣,揹包容量不變,即a = val[i-1][j];

    ii)裝的話,最大總價值為前面最大總價值加上當前價值,揹包容量減去該物品質量,即 b = val[i-1][j-w[i]]+v[i];

則,val[i][j]=max(a,b).

2 示例

編號1-6的物品資訊為:重量陣列w = {4, 6, 2, 2, 5, 1},價值陣列v = {8, 10, 6, 3, 7, 2}。求揹包容量C = 12時,選哪幾件物品總價值最大?最大價值是多少?

求C=12的過程依賴於前面各個過程結果,列出價值矩陣如下(自下而上、自左向右求得),所以當c=12時的最大價值為24。

那具體放入了那幾件物品呢?看最後一列,從最後一個逐個和上一個值做比較,如果相等,說明該物品沒有放入揹包,如果不等,則放入了揹包。容易看出揹包中的物品為第3,2,1號。

0

1

2

3

4

5

6

7

8

9

10

11

12

1

0

0

0

8

8

8

8

8

8

8

8

8

2

0

0

0

8

8

10

10

10

10

18

18

18

3

0

6

6

8

8

14

14

16

16

18

18

24

4

0

6

6

9

9

14

14

17

17

19

19

24

5

0

6

6

9

9

14

14

17

17

19

21

24

6

2

6

8

9

11

14

16

17

19

19

21

24

3 C++實現

迴圈或遞迴均可,註釋已標註

a.迴圈實現

#include<iostream>

using namespace std;

int main(){
	
	int N=6;
	int C=12;
	
	int v[N+1]={0,8,10,6,3,7,2};
	int w[N+1]={0,4,6,2,2,5,1};
	

	int val[N+1][C+1];//價值矩陣 
	
	//針對第0個物品,無論揹包大小是多少,所能取得的最大價值均為0 
	for(int j=0;j<C+1;j++) val[0][j]=0;
	
	
	for(int i=1;i<=N;i++){
		for(int j=1;j<=C;j++){
			//如果當前揹包容量j小於當前物品i的重量,則不裝i物品 
			if(j<w[i]) val[i][j]=val[i-1][j];
			
			//如果當前揹包容量大於等於當前物品i的重量,則選擇裝它或不裝它中較大的值 
			else val[i][j]=max(val[i-1][j-w[i]]+v[i],val[i-1][j]);
		}
	}
	cout<<val[N][C]<<endl;
	

	//從高到低,逐個判斷是否裝在了揹包中
	//判斷方法:逐個與前一個比較,若價值相等,說明該物品未裝入包中 
	int b[N+1];
	for(int i=N;i>=1;i--){
		if(val[i][C]==val[i-1][C]) b[i]=0;
		else{
			b[i]=1;
			cout<<i<<' ';
			C=C-w[i];
		}
	}

	return 0;
}

b.遞迴實現

#include<iostream>

using namespace std;

//遞迴實現,返回第1至第i個物品可裝入的最大價值,當前揹包容量c,w[]為重量,v[]為對應的價值 
int val(int i,int c,int w[],int v[]){
	//第0個物品價值為0 
	if(i==0) return 0;
	
	//如果當前揹包容量小於當前物品重量,則不放入,最大價值同val(i-1,c,w,v) 
	else if(c<w[i-1]) return val(i-1,c,w,v);	
	
	//如果當前揹包容量大於等於當前物品重量,放入或不放入,看哪個價值大 
	else{
		int a=val(i-1,c-w[i-1],w,v)+v[i-1];//放入後總價值 
		int b=val(i-1,c,w,v);//不放入總價值 
		return max(a,b);
	}
}


int main(){
	int v[6]={8,10,6,3,7,2};
	int w[6]={4,6,2,2,5,1};
	
	int m=6;//第m個物品 
	int c=12;//揹包容量 
	cout<<val(m,c,w,v)<<endl;//最大價值 
	
	//從高到低,逐個判斷是否裝在了揹包中
	//判斷方法:逐個與前一個比較,若價值不相等,說明該物品放入了包中
	for(int i=6;i>=1;i--){		
		if(val(i,c,w,v)!=val(i-1,c,w,v)){
			cout<<i<<' ';
		} 		
	}	
	
	
	return 0;
} 

參考: