日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言dp算法解决背包问题,DP求解完全背包问题及其优化原理

發(fā)布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言dp算法解决背包问题,DP求解完全背包问题及其优化原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1.?完全背包問題的形式化描述

完全背包問題是一類經(jīng)典的DP(Dynamic Programming,動態(tài)規(guī)劃)問題,問題描述如下:

有n種重量和價值分別為wi,vi的物品,從這些物品中挑選出總重量不超過W的物品,求所有挑選方案中價值vi總和的最大值。值得注意的是,每種物品都可以選取無限次。

假設(shè)第i種物品選擇了ki個,ki∈N,則

目標(biāo)函數(shù)為:

max{Σkivi}

約束條件為:

$?\sum k_i w_i \leq W $

2. 完全背包問題的平凡解法

首先對所有物品種類進(jìn)行1~n的編號。

設(shè)函數(shù)F(i,j),其意義為:從編號1~i的種類中選取物品,裝入最大允許重量為j的背包中,所能獲得的最大總價值是F(i,j)。

首先考慮基準(zhǔn)情況:選0個物品,即i=0,這時:

F(i,j) = F(0,j) = 0

對于子問題F(i,j)的求解,其最優(yōu)解的決策依賴于涉及1~(i-1)類物品的子問題F(i-1,j)。

當(dāng)前要做出決策,需選k個i類物品(k可以為0),根據(jù)物品的重量分為如下兩種情況:

1。若物品重量wi不超過背包的允許重量j,則可分別選0個、...、k個該物品,kwi都不超過允許重量j。最后再從所有選擇結(jié)果中挑選最大總價值,作為當(dāng)前最優(yōu)解。

F(i,j) = max{ F(i-1,j - k ×?wi) + k ×?vi|?(k>=0,kwi<=j)}

2。若物品重量wi超過背包的允許重量j,則無法放入當(dāng)前物品:

F(i,j) = F(i-1,j)

上述兩個遞推式揭示了當(dāng)前狀態(tài)與上一狀態(tài)之間的轉(zhuǎn)移關(guān)系。加上基準(zhǔn)情況,我們發(fā)現(xiàn)算法已經(jīng)封閉了,可以編程實現(xiàn)。

核心程序

輸入:物品種類數(shù)n,背包最大允許重量t,第i種物品重量w[i]、價值v[i]。

輸出:最大價值dp[n][t]。

初始化:清零dp數(shù)組

for(int i=1;i<=n;++i) {for(int j=0;j<=t;++j) {if(j

dp[i][j]=dp[i-1][j];

}else{for(int k=0;k*w[i]<=j;++k) {

dp[i][j]=max(dp[i][j], max(dp[i-1][j], dp[i-1][j-k*w[i]]+k*v[i]));

}

}

}

}

可以看出漸近復(fù)雜度O(n×t×w),為三次復(fù)雜度,因此該程序能解決的問題規(guī)模非常有限。但正如標(biāo)題所說的,平凡解法還有較大可優(yōu)化的空間。

3. 利用數(shù)據(jù)相關(guān)性——時間復(fù)雜度優(yōu)化

利用循環(huán)間數(shù)據(jù)相關(guān)性優(yōu)化算法是一種常見的技巧。例如“最大子段和”問題的蠻力解法中,利用數(shù)據(jù)相關(guān)性可以將O(n^3)的復(fù)雜度直接優(yōu)化為O(n^2)。同理,在求Σnx=1?x!(連續(xù)階乘和)問題中,利用數(shù)據(jù)相關(guān)性可將O(n^2)直接優(yōu)化為O(n)。這些例子展現(xiàn)了該技巧的生命力。

回到我們的DP解法,觀察for(j)與for(k)循環(huán),猜測兩個循環(huán)間存在數(shù)據(jù)相關(guān)性,例如:F(i-1,j)中選擇k個物品的情況,與F(i-1,j-wi)中選擇k-1個情況完全相同,推測F(i-1,j)的遞推中k>=1的部分在計算F(i-1,j-wi)時就已經(jīng)求出了,這意味著k循環(huán)除了第一趟執(zhí)行對結(jié)果有貢獻(xiàn)外,后續(xù)執(zhí)行只是在重復(fù)之前已經(jīng)完成的計算,有希望將k循環(huán)合并到j(luò)循環(huán)中。

為了驗證這種想法,推導(dǎo)如下:

題設(shè):

F(i,j) = max{F(i-1,j - k?×?wi) + k?×?vi|(k>=0)}? ……①

考慮當(dāng)k==0時,F(i-1,j - k?×?wi) + k?×?vi退化為F(i-1,j),所以

F(i,j) =?max(F(i-1,j), max{F(i-1,j - k?×?wi) + k?×?vi|(k>=1)} )

為了驗證之前的猜測,將k代換為k+1。問題等價變形到k>=0的情況:

F(i,j) =?max(F(i-1,j), max{F(i-1,j - (k+1) ×?wi) + (k+1) ×?vi|(k>=0)})

=?max(F(i-1,j), max{F(i-1,(j -?wi)-?k ×?wi) + k ×?vi+?vi|(k>=0)} )? ??……②

結(jié)論仍不明顯,將上式②下劃線部分提到max{}后面,發(fā)現(xiàn):

F(i,j) =?max(F(i-1,j), max{F(i-1,(j -?wi)-?k ×?wi) + k ×?vi|(k>=0)}?+vi)

對比①知,上式藍(lán)色部分恰等價于F(i, j -?wi),這基本驗證了我們的想法。

F(i,j) =?max(F(i-1,j),?F(i, j -?wi)?+?vi)

這便是最終的表達(dá)式,可以看到成功消去了k。

根據(jù)推導(dǎo)的狀態(tài)轉(zhuǎn)移方程編寫程序,時間復(fù)雜度直接從三次降至了二次。

需要注意,因為F(i-1,j)依賴于F(i-1,j-wi)的計算結(jié)果,所以j必須遞增枚舉,才能保證構(gòu)造的正確性。

for(int i=1;i<=n;++i) {for(int j=0;j<=t;++j) {if(j

dp[i][j]=dp[i-1][j];

}else{

dp[i][j]=max(dp[i-1][j], dp[i][j-w[i]]+v[i]);

}

}

}

4. 降低dp數(shù)組維度——空間復(fù)雜度優(yōu)化

優(yōu)化前,dp數(shù)組的空間復(fù)雜度是O(n×t)的。

注意到dp數(shù)組中,dp[i]行的各元素值只依賴于前一行dp[i-1],而不依賴于dp[i-2]、……、dp[0]行。這啟發(fā)我們只存儲一行dp數(shù)組,然后在該行“原地”更新數(shù)據(jù)。

仍然有兩種情況:

1。對于j

2。對于其它非1。的情況,需要更新dp行,dp[j]=max(dp[j], dp[j-w[i]]+v[i]); 加粗部分反映了“原地”更新策略。

for(int i=1;i<=n;++i) {for(int j=w[i];j<=t;++j) {

dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

}

}

因為情況1。的存在,j的取值必須以w[i]為區(qū)間起點。

值得注意的是,j循環(huán)按遞增順序枚舉,這與我們在第3節(jié)中得出的結(jié)論一致。

經(jīng)過優(yōu)化,空間復(fù)雜度降至了O(n))。

5. 結(jié)論

首先描述了完全背包問題,然后分析了最直觀的trivial解法,由平凡解法發(fā)現(xiàn)循環(huán)間的數(shù)據(jù)相關(guān)性,推導(dǎo)出優(yōu)化后的狀態(tài)轉(zhuǎn)移方程,推導(dǎo)結(jié)論使算法時間復(fù)雜度降至O(n×t)。接下來從另一角度——空間復(fù)雜度分析dp數(shù)組降維優(yōu)化,使空間復(fù)雜度降低至O(n)。綜合時空兩個方面,得出了DP求解此類問題的優(yōu)化方法。

總結(jié)

以上是生活随笔為你收集整理的c语言dp算法解决背包问题,DP求解完全背包问题及其优化原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。