動態規劃法解決0/1揹包問題詳解
阿新 • • 發佈:2019-02-12
是什麼
動態規劃(dynamic programming)是求解決策過程最優化的數學方法,把多階段過程轉換為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法。基本思想
將待求解的問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。
求解步驟
- 找出最優解的性質,刻畫出結構特徵
- 遞迴的定義最優解的值
- 自底向上的方式計算最優值(例如0/1揹包問題將價值從低到高排序,再依次像揹包中放入,調整)
- 根據最優值構造最優解(求出哪些物品放在了揹包中,標記為1,否則標記為0)
0-1揹包問題
問題描述
現在有5個物品,第i個物品的價值為vi,重量為wi,揹包的容量為W,其中的引數都為非負數,W=17。求解如何放物品使得揹包的總價值最大。假設我們將物品的價值從低到高排好序如下表:物品編號 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
價值v | 4 | 5 | 10 | 11 | 13 |
重量w | 3 | 4 | 7 | 8 | 9 |
問題分析
- 嘗試將1號物品放入揹包
I\W | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
- 嘗試將2號物品放入揹包
I\W | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
- 嘗試將3號物品放入揹包
I\W | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
3 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 10 | 10 | 14 | 14 | 14 | 14 | 19 | 19 | 19 | 19 |
- 嘗試將4號物品放入揹包
I\W | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
3 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 10 | 10 | 14 | 14 | 14 | 14 | 19 | 19 | 19 | 19 |
4 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 11 | 11 | 14 | 15 | 16 | 16 | 19 | 21 | 21 | 21 |
- 嘗試將5號物品放入揹包
I\W | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
3 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 10 | 10 | 14 | 14 | 14 | 14 | 19 | 19 | 19 | 19 |
4 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 11 | 11 | 14 | 15 | 16 | 16 | 19 | 21 | 21 | 21 |
5 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 10 | 11 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 23 | 24 |
程式碼示例
- 遞迴定義最優解的值
- 計算揹包最優解的值
int **KnasackDP(int n,int* Weights,float* Values){
int i,w; /*物品編號和揹包剩餘容量*/
/*為二維陣列申請空間*/
int** c=(int**)malloc(sizeof(int*)*(n+1));
/*宣告一個一維陣列存放物品*/
for(i=0;i<=n;i++)
c[i]=(int*)malloc(sizeof(int)*(W+1));
/*初始化二維陣列*/
for(w=0;w<=W;w++)
/*0個物品放入揹包中,價值為0*/
c[0][w]=0;
for(i=1;i<=n;i++){
/*i個物品放到容量為0的揹包中,價值為0*/
c[i][0]=0;
for(w=1;w<=W;w++){
if(Weights[i-1]<=w){ /*揹包剩餘容量大於物品重量*/
/*將當前物品放入揹包的最大價值>不放的最大價值*/
if(Weights[i-1]+c[i-1][w-Weights[i-1]]>c[i-1][w])
{
/*重量為w的揹包中放入該物品*/
c[i][w]=Values[i-1]+c[i-1][w-Weights[i-1]];
}
else{
/*重量為w的揹包中不放入該物品*/
c[i][w]=c[i-1][w];
}
}
else
/*若物品重量不小於揹包容量w,則不放入該物品
c[i][w]=c[i-1][w];
}
}
/*返回最優值*/
return c;
}
- 構造最優解
void OutputKnapsackDP(int n,int W,int *Weights,float *Values,int **c){
int x[n];
int i;
for(i=n;i>1;i--){
if(c[i][W]==c[i-1][W])
/*物品i沒有放入揹包,存0*/
x[i-1]=0;
else{
/*物品i放入揹包,存1*/
x[i-1]=1;W=W-Weights[i-1];
}
}
if(c[1][W]==0)
/*第一個物品不放入揹包*/
x[0]=0;
else
/*第一個物品放入揹包*/
x[0]=1;
for(i=0;i<n;i++)
if(x[i]==1)
/*打印出放入揹包的物品重量和對應價值*/
printf("Weigh:%d,Value:%f\n",Weights[i],Values[i]);
}