咋先不說解決這種揹包問題的方法,個人感覺應該先把產生問題的背景描述出來會比較好一點,如下:
1,問題描述
有N件物品和一個容量爲V的揹包。第i件物品的重量是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的重量總和不超過揹包容量,且價值總和最大。,
2,具體實例:
現有一個容量爲10kg的揹包,還有5件不同質量和價格的物品,現在讓你,把這些物品有選擇性的放到揹包裏面,使得揹包的價值最大且揹包裏物品的重量不能超過揹包的最大容量。5件物品的重量分別爲{A,B,C,D,E}與之對應的質量分別爲{2,2,6,5,4}對應的價格爲{6,3,5,4,6}。
問題特點:
每種物品僅有一件,可以選擇放或不放。
解決問題思路:
我把揹包容量從0列舉到10,因爲揹包容量太大,所以求解很複雜。故此,我就分別求,看揹包容量分別爲0,1,2,3....10時,怎樣放物品能使不同容量的揹包價值最大。放物品的時候也是一個一個的放,至於是否放得進去,看如下例子:
(想了好久也不知道該怎麼敘述,這裏我就直接舉例說明,看我基友能不能看懂,要是他能看懂的話我,相信你們就能看懂)
現在,我準備把物品A往揹包裏面放,容易看出,揹包容量爲0和1時,物品A是放不進去的,所以價值爲0,當揹包容量爲2,3,4....10時可以放進去,且與之對應的價值都是6,這應該好理解吧,就是說,揹包容量爲2時,把A放進去,揹包滿了,價值爲6,容量爲3時,A放進去時,揹包沒滿,但價值還是6,以此類推後面的容量對應的價值都是6。如圖,
現在,在以上操作的基礎上(物品A放入相應容量的揹包中使得揹包的價值最大)再把物品B放入相應容量的揹包中,由於B的重量爲也是2,所以揹包容量爲0和1時不能放B,所以對應的價值,應該是上一次(放入A)時的最大價值,結果還是0,然後容量爲2時,做出判斷,如果放入B,揹包滿了不能放A了,價值是3,如果不放B,那麼價值就是A的價值6,很顯然6大於3 所以這兒應該放A,然後一直往後放,容量爲4時,可以把A和B都放進去,價值爲9,以後容量爲5,6,7,8,9,10價值都是9 如下圖
好了,現在可以把C也放進去了如圖
最後把D和E也放進去
至此,所有的物品都已有選擇性的放進去了,但是你還不知道到底哪個是真的放進去了,哪個沒放進
不過,最大值可以看出來,就是15
找出最優解:
判斷放進去的物品:
我們從最大值開始判斷,揹包容量爲10時,(有選擇性的放或不放E後)價值最大爲15,(有選擇性的放D或不放D後)價值最大爲14,這就說明了一個問題,要是沒有放E,那麼最大價值應該是14,所以確定E肯定放進去了。
然後判斷D是否放入了揹包,判斷D時,先把E的重量減掉(因爲E已經確定放進去了,所以接下來判斷的就是剩餘揹包容量中是否放入了D),10-4=6,看揹包容量爲6時的情況,有圖可以看出,對D沒有進行任何操作時的價值爲9(看C行,容量爲6的那個價值),然後對D進行 ”有選擇性的放或不放“ 的操作後,價值還是9,那說明D肯定沒放,
對C判斷,同樣的,先算出剩餘揹包容量,由於已近確定放E,沒放D,所以剩餘揹包容量爲10-4=6,有圖可知,對C沒有進行任何操作時的價值爲9(看B行,容量爲6的那個價值),然後對C進行 ”有選擇性的放或不放“ 的操作後,價值還是9,那說明C肯定沒放,
對B判斷,以上可知,C,D都沒放,放入了E,揹包剩餘容量還是6,對B沒有進行任何操作時的價值爲6(看A行,容量爲6的那個價值),然後對B進行 ”有選擇性的放或不放“ 的操作後,價值是9,那說明C肯定放進去了,
對A判斷,以上可知剩餘揹包容量爲10-4-2=4,對A沒有進行任何操作時的價值爲0(啥都不操作的時候),然後對A進行 ”有選擇性的放或不放“ 的操作後,價值是6,那說明A肯定放進去了,
至此我們可以得出我們的最優解揹包裏放進去的物品分別是A,B,E,還可以知道,揹包容量爲2+2+4=8時,揹包的價值就已經達到最大了。
下面附上代碼
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
const int c = 10; //揹包的容量
const int w[] = {0,2,2,6,5,4};//物品的重量,其中0號位置不使用 。
const int v[] = {0,6,3,5,4,6};//物品對應的待加,0號位置置爲空。
const int n = sizeof(w)/sizeof(w[0]) - 1 ; //n爲物品的個數
int x[n+1];
void package0_1(int m[][11],const int w[],const int v[],const int n)//n代表物品的個數 用於進行“有選擇性放或不放物品” 的操作
{
//採用從底到頂的順序來設置m[i][j]的值
//首先放w[n]
int i,j;
memset(m,0,sizeof(m));
for(i = 1; i <= n; i++)
{
for( j = 0; j <= c; j++)
{
m[i][j] = m[i-1][j];//如果j < w[i]則,當前位置就不能放置,它等於上一個位置的值。
//否則,就比較到底是放置之後的值大,還是不放置的值大,選擇其中較大者。
if(j>=w[i])
{
m[i][j] = m[i-1][j] > m[i-1][j-w[i]] + v[i]?
m[i-1][j] : m[i-1][j-w[i]] + v[i];
}
}
}
}
void answer(int m[][11],const int n) //選出最優解,判斷那個放入了揹包,那個沒有放,
{
int j = c;
int i;
for(i = n; i>=1; i--)
if(m[i][j] == m[i-1][j])
x[i] = 0;
else
{
x[i] = 1;
j = j - w[i];
}
}
int main()
{
int m[6][11];
memset(m,0,sizeof(m));
package0_1(m,w,v,n);
for(int i = 0; i <= 5; i++)
{
for(int j = 0; j <= 10; j++)
if(i==0)
printf("%2d ",j);
else
printf("%2d ",m[i][j]);
printf("\n");
}
answer(m,n);
printf("The best answer is:\n");
for( i = 1; i <= 5; i++)
printf("%d ",x[i]);
system("pause");
return 0;
}
- m[i][j] = m[i-1][j] > m[i-1][j-w[i]] + v[i]?
- m[i-1][j] : m[i-1][j-w[i]] + v[i];
對於該式子的理解,首先得清楚一點就是每個物品只有兩種狀態 放或不放 例如那物品A和B來說,相互組合之後有三種情況
(1)放A不放B (2)放B不放A (3)即放A又放B
其中怎麼判斷到底選擇哪種放發 主要就看上面這個公式,看左面,其中m [i-1] [j]表示的就是放A不放B時的揹包的價值 m[i-1][j-w[i]]+v[i]這個式子有兩層意思:只放B A與B都放,具體的實現就要看是否滿足對應的條件了,這裏面,給我的感覺是,它直接假設把第i(這裏是物品B)個物品放進去,下面我給你分析一下:
放B後(第i個物品),那麼揹包剩餘的質量就是j-w[i],然後看前i-1(在這裏只有物品A)個物品在剩餘的揹包質量中的價值是多少,即m[i-1][j-w[i]]。最後加上物品第i個(這裏是物品B)物品的價值v[i],最終的公式是m[i-1][j-w[i]]+v[i]
j=2,3時
具體的例子是,現在開始放B,即判斷m[2][j](j的範圍是0到10)的值,我們一個一個的判斷,因爲B的質量爲2,所以只有揹包的質量等於2 時纔開始進行方與不放的操作,那麼現在使m[2][2]等於m[1][2]還是m[1][0]+v[1] 這裏面m[1][2]的意思就是放A不放B ,那麼價值就是A的價值=6,然後看m[1][0]+v[1]這裏我前面說了,直接假設把B放進去,那麼揹包剩餘質量爲j-w[i]=0,由此可知前i-1個物品(在這裏只有A)是放不進去的,那麼價值爲0,然後加上第i個物品的價值v[i]=3,所以最終的價值m[i-1][j-w[i]]+v[i]=3其中表達的意思是放B不放A,最終判斷m[1][2]=6大於m[1][0]+v[1]=3所以m[2][2]=6,後面的j=3,判斷方法於此一樣
j=4,5,6,7,8,9,10時
這裏,再說一下j=4時的情況,現在使m[2][4]等於m[1][4]還是m[1][2]+v[4] 這裏面m[1][4]的意思就是放A不放B ,那麼價值就是A的價值=6,然後看m[1][2]+v[4]這裏我前面說了,直接假設把B放進去,那麼揹包剩餘質量爲j-w[i]=2,由此可知前i-1個物品(在這裏只有A)是可以放進去的,那麼價值爲6,然後加上第i個物品的價值v[4]=3,所以最終的價值m[i-1][j-w[i]]+v[i]=9其中表達的意思是A和B都放進去,最終判斷m[1][4]=6小於m[1][2]+v[4]=9所以m[2][4]=9.後面的j=5,6,7,8,9,10 的判斷方法於此一樣