說明:演算法源自教材。本文相當於對教材做的乙個筆記(動態規劃與貪心演算法解01揹包必須先對揹包按照單位重量的**從大到小排序,否則拆分的子問題就不具備最優子結構的性質)
動態規劃演算法:
動態規劃就是乙個填表的過程。該錶記錄了已解決的子問題的答案。求解下乙個子問題時會用到上乙個子問題的答案。
思路:我們可以將1,2,3,4,5子問題的答案都存入一張表中。因為求解2子問題,需要用到1子問題的答案(2的每一步方案要與1的每一步方案比較,如何2的該步方案優於1所對應的方案。則將2的這步方案標為可行。如果不優於1的,或者不滿足問題的約束條件,則捨棄該方案。繼續沿用該步所對應的1的方案作為該步的方案)。求解3子問題,需要用到2子問題的答案,一直遞推到求解5子問題,需要用到4子問題的答案。而5子問題就是原問題。5子問題的答案就是最終原問題的解。
解法:
以01揹包問題為例,作講解。給出問題引數:
c=10; //揹包容量c
n=5; //物體個數n
int w[6]=; //物重w,0無意義,只是為方便描述問題而已
int p[6]=; //物價p
執行的最終結果是張二維表:(f[i][j],i就是n,j就是c)
n\c012
3456
78910
0000
0000
0000
1006
6666
6666
2006
6999
9999
3006
6999
911111440
0669
991011
131450
0669
9121215
1515
1.程式是一行一行的進行填表的。f[0][0,1,2,3,4.....]......f[5][0,1,2,3,4....] (程式初始化的過程就是將第0行的所有數填為0,這是符合現實情況的。它表明當前揹包沒有裝物品,當前揹包的價值是0)
2.拿f[3][8]=11來說明填表的過程。f[3]表明i=3(當前子問題有3個物品可選,分別是1,2,3號物品),f[3][*]的值就是第3個子問題的解。我要選的3號物品的重量是6,它的價值是5,所以我會找到它的前6列的上一行所對應的揹包的價值(f[2][2])+5(當前要選的3號物品的價值)=11,值為11>f[2][8]=9,代表我選了1,3的方案要優於選1,2的這種方案,所以我將11填入到**【如果f[2][2]+5<9,則將9填入**】-------------------這裡需要說明一下f[2][8]=9的含義:2子問題的乙個解是選擇1,2號物品所對應的揹包價值為9。------恰巧我們在解決3子問題時需要計算比較這幾種方案,選1,2號》背價=?,揹包**是多少,>>>>背價=?,>>>>背價=?>揹包**在2子問題中已給出,因此我們可以在3子問題中直接用。為何不考慮呢?就是拿2號和3號組隊放入揹包?因為在2子問題中已經記載了:【如果要單選乙個揹包的話,選擇1號獲得的價值要比2號獲得的價值大----我們追溯到f[2][2]=6是整麼來的,揹包容量是2的時候,[1,2]號物品只能有乙個放入揹包,放入1,揹包的價值是6,f[1][2]=6的計算是在1子問題中已經求解出來的,2子問題可以直接用該值,而不用再重複計算。放入2號,背值是3,3<6,所以沿用上乙個子問題的解作為該步的答案,所以f[2][2]是6,而不是3,所以它相當於定義說:下乙個子問題在求解的過程中,如果遇到只能從2號和1號物品中選擇乙個物品裝入揹包時,請選擇1號物品】
3.其實將上述的描述寫成**,就是兩層for迴圈(遍歷n,再巢狀遍歷c),加上兩個判斷(1.當前子問題的可行的乙個方案與上乙個子問題的對應的可行方案誰最優。2.連續變數j的值是否達到了跳躍點的值,達到了才更新當前揹包的價值):**如下
#include#includeusing namespace std;
void knapsack(int n,int c,int *w,int *p)
} }//因為我是i++和j++,迴圈結束,多加了一次i和j,所以最後要減回來
cout
cout
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++)
*/c=10; //揹包容量c
n=5; //物體個數n
int w[6]=; //物重w
int p[6]=; //物價p
knapsack(n,c,w,p);
}
如何進一步優化演算法:
上述演算法有兩個明顯的缺點:
其一是演算法要求所給物品的重量w[i]是整數.
其次,當揹包容量很大時,演算法需要的計算時間較多,當c>2^n,演算法需要n*2^n這麼多的計算時間.
優化思路:
程式執行的資料流圖如下:
最後放一下優化後的演算法的**實現:
#includeusing namespace std;
template//void traceback(int n, type w, type v, type **p,int *head)
}} //cout
int p[100][2];
int head[7];
//int *head = new int[n + 2];
head[n + 1] = 0;
p[0][0] = 0;
p[0][1] = 0;
int left = 0, right = 0, next =1;
head[n] = 1;
//w=[2,2,6,5,4]
//v=[6,3,5,4,6]
//第一次:left=1,right=2,head[4]=next=3
//依次找出子問題的活躍點集(,,,,備註:5,4,3,2,1是物品的標號)
//1號迴圈
for (int i = n; i >= 1; i--)
while (k <= right && p[k][1] <= p[next - 1][1])
k++;
}while (k <= right)
left = right + 1;
right = next - 1;
head[i - 1] = next;
} traceback(n, w, v, p, head);
//cout <
return p[next - 1][1];
}int main();
int v[11]=;
//int **p=new int[2];
int c=knapsack(5,10,v,w);
// int w[7]=;
// int v[7]=;
// int c=knapsack(4,8,v,w);
cout <
//cout <
}
後記:
演算法的時間複雜度分析: 優化前:o(nc) 優化後:o(min)
01揹包問題 (動態規劃演算法)
0 1 揹包問題 給定 n 種物品和乙個容量為 c 的揹包,物品 i 的重量是 wi,其價值為 vi 問 應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?分析一波 面對每個物品,我們只有選擇拿取或者不拿兩種選擇,不能選擇裝入某物品的一部分,也不能裝入同一物品多次。解決辦法 宣告乙個 大...
01揹包問題 (動態規劃演算法)
題目 給定n種物品和乙個容量為v的揹包,物品i的體積是wi,其價值為ci。每種物品只有乙個 問 如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?面對每個物品,我們只有選擇放入或者不放入兩種選擇,每種物品只能放入一次。我們用之前同樣的思路來走一遍試試 假設只剩下最後一件物品,我們有兩種選擇 ...
動態規劃演算法 01揹包問題
動態規劃演算法通常用於求解具有某種最優性質的問題。在這類問題中,可能會有許多可行解。每乙個解都對應於乙個值,我們希望找到具有最優值的解。動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,適合於用動態規劃求解的...