动态规划之买瓜子—C说算法系列
熟能生巧,大多數(shù)的解題步驟或者思路都可通過(guò)訓(xùn)練獲得。將事情重復(fù)做,即使剛開(kāi)始你感覺(jué)素手無(wú)錯(cuò),一臉茫然,但是通過(guò)重復(fù)訓(xùn)練,最終也能夠游刃有余。
題目來(lái)源:第十二屆藍(lán)橋杯青少年國(guó)賽C++中級(jí)組-編程題4
這是一道DP(動(dòng)態(tài)規(guī)劃)的題目,大家都知道,DP算法的效率極高,但是較難理解。在很多算法的題解中對(duì) DP在該題中的應(yīng)用思路? 只簡(jiǎn)單提了幾句。我們來(lái)看一道題,或許題解過(guò)程能讓你對(duì)DP的理解更加深入一些。
題目?jī)?nèi)容:
校慶,采購(gòu)瓜子。資金N(1<=N<=1000)元,M(1<=M<=30)種瓜子。問(wèn)最多能采購(gòu)多少千克的瓜子?比如N=80元,M=2種。第1種,每袋18元10千克;第2種,每袋30元20千克。
輸入樣例:
80 2
18 10
30 20
輸出樣例
50
提示:18+30+30=78元 10+20+20=50千克
分析:
這是完全背包問(wèn)題,是一道模板題,是必須要掌握的(背代碼也要背下來(lái))。那什么是完全背包問(wèn)題呢?
完全背包問(wèn)題:一般是指,有N件物品和一個(gè)能背重量為W的背包,第i件物品的重量為weight[i],價(jià)格為value[i]。每件物品有無(wú)限個(gè)(也就是可以放入背包多次),求怎樣可以使背包物品價(jià)值總量最大。
本道題我們完全可以套模板。資金N元(按照完全背包問(wèn)題的解釋,我們可以把N看成W),即有M種瓜子,有資金N元,求怎樣可以使買(mǎi)的瓜子重量最多?
動(dòng)態(tài)規(guī)劃算法最重要的兩點(diǎn)是:
1、確定dp數(shù)組和下標(biāo)的含義? ? ? ? 2、確定遞推公式(選或者不選物品)
本題中的dp數(shù)組我們可以這樣定義:int dp[50][1010];?
dp[i][w]代表 - 前i個(gè)物品,在價(jià)值為w的情況下,最大的重量是dp[i][w]
因?yàn)镸<=30,物品的數(shù)量不會(huì)大于30,所以dp的第一個(gè)維度,我們?cè)O(shè)置一個(gè)大于30的長(zhǎng)度即可。資金W<=1000,dp的第二個(gè)維度代表資金,資金<=1000,所以第二個(gè)維度我們?cè)O(shè)置一個(gè)大于1000的長(zhǎng)度即可。
該題中涉及到的數(shù)據(jù)結(jié)構(gòu)有:
數(shù)據(jù)的輸入
輸入資金和瓜子種數(shù)
?
數(shù)據(jù)的處理
此步是最核心最關(guān)鍵的,我們定義了dp[i][w]數(shù)組,該數(shù)組的含義代表前i個(gè)物品,在價(jià)格為w的情況下,最大的重量是dp[i][w],對(duì)于本題的例子當(dāng)N=80,M=2的情況下,最多能買(mǎi)50kg的瓜子。即求出dp[2][80]的值。
在dp中,要求出dp[i][j]的值,需要通過(guò)遞推計(jì)算而來(lái)。
注意:要認(rèn)真區(qū)別此處各種符號(hào)表示的含義。
對(duì)于每一種瓜子,都有選與不選兩種選擇。dp數(shù)組的初始化,當(dāng)i=0或者w=0的情況下,dp[i][w]均為0,因?yàn)閐p[0][w]代表前0件商品,在價(jià)格為w的情況下,最大的重量,既然沒(méi)有商品,那么最大的重量肯定為0,dp[i][0]代表前i件商品,在資金為0下,最大的重量,既然沒(méi)有資金,那么最大的重量肯定為0.
以資金=80,種類(lèi)=2為例,有:
dp[0][0],dp[0][1],dp[0][2],dp[0][3],dp[0][4]……dp[0][80]的值均為0.
dp[0][0],dp[1][0],dp[2][0],dp[3][0],dp[4][0]……dp[50][0]的值均為0.
可通過(guò)嵌套for循環(huán)進(jìn)行遞推,利用for循環(huán)求出dp[i][w]
在當(dāng)前資金j不夠買(mǎi)第i種瓜子的情況下,dp[i][j]的值等于dp[i-1][j]的值,dp[i-1][j]代表前i-1種瓜子,在價(jià)格為j下,最大的重量為dp[i-1][j].
比如對(duì)于dp[1][16],在前1種瓜子下,當(dāng)前資金是16元,但是根據(jù)題目意思,第1種瓜子的價(jià)格為18,所以dp[1][16]=dp[0]=16]=0千克,無(wú)法買(mǎi)瓜子,所以最大重量為0
再比如對(duì)于dp[1][18],在前1種瓜子下,當(dāng)前資金是18元,第1種瓜子的價(jià)格剛好為18元,資金足夠,根據(jù)代碼dp[1][18]=max(dp[0][18],dp[1][0]+10)=10千克,所以最大重量為10千克
再比如對(duì)于dp[2][19],,在前2種瓜子下,當(dāng)前資金是19元,當(dāng)前第2種瓜子的價(jià)格為30元,資金不夠,因此dp[i][j]=dp[i-1][j]=dp[2][19]=dp[1][19]=10千克
對(duì)于每一種瓜子,只有買(mǎi)和不買(mǎi)兩種選擇,對(duì)于前i種瓜子,在現(xiàn)有資金為j下,如果資金足夠,我們可以買(mǎi),也可以不買(mǎi),那到底買(mǎi)不買(mǎi)呢?
買(mǎi)不買(mǎi)取決于:如果不買(mǎi)的重量比買(mǎi)了的重量更大,就不買(mǎi),如果買(mǎi)了的重量比不買(mǎi)的重量更大,就買(mǎi)。本題例子的輸入,不好解釋dp[i][j]=max(dp[i-1][j],dp[i][j-wt[i]]+v[i]);這一現(xiàn)象,我們可以更改一下輸入,即?
80 2
30 20
18 10
比如要求:dp[2][30]=max(dp[1][30],dp[2][30-18]+v[2])=max(20,10)=20,前2種瓜子,當(dāng)資金為30,最大的重量為20.因?yàn)楫?dāng)資金30時(shí),如果不選擇買(mǎi)18元的瓜子,就有20千克,如果選擇買(mǎi)18元的瓜子,最終的重量就只有10千克
注意:這里是一種瓜子可以被選擇多次。
數(shù)據(jù)的輸出
n代表瓜子的數(shù)量,w代表錢(qián)的數(shù)量,對(duì)于本道題而言就是dp[2][80]=50?
遞推表格(演示樣例,當(dāng)資金=80元,瓜子種類(lèi)數(shù)為2種時(shí),最大的重量):
?
代碼實(shí)現(xiàn):
#include<bits/stdc++.h> using namespace std;int dp[50][1010]; //表示,前i個(gè)物品,在價(jià)值w的情況下,最大的重量是dp[i][w] int wt[1010],v[50]; //wt是價(jià)格,v是重量 int n,w; int main() {cin>>w>>n; for(int i=1;i<=n;i++)cin>>wt[i]>>v[i]; //wt[i]代表第i種瓜子多少錢(qián) v[i]代表第i種瓜子多少千克 for(int i=1;i<=n;i++) //遍歷n種瓜子,每種要么選,要么不選 {for(int j=1;j<=w;j++) //j是價(jià)格 {if(j>=wt[i]) //wt是價(jià)格 {if(dp[i-1][j]>dp[i][j-wt[i]]+v[i]){dp[i][j]=dp[i-1][j];}else{dp[i][j]=dp[i][j-wt[i]]+v[i];}}else{dp[i][j]=dp[i-1][j]; }}}cout<<dp[n][w]<<endl;return 0; }總結(jié):
動(dòng)態(tài)規(guī)劃的題目還是要多做,做著做著自然會(huì)有靈感,如果你一開(kāi)始覺(jué)得很難,就不去動(dòng)手敲代碼,那你永遠(yuǎn)也學(xué)不會(huì)動(dòng)態(tài)規(guī)劃,不僅動(dòng)態(tài)規(guī)劃,其它題目也是如此。最后送給大家一句話(huà)學(xué)如逆水行舟不進(jìn)則退。
大家可以進(jìn)微信群討論數(shù)據(jù)結(jié)構(gòu)與算法,也有資源分享給大家:微信號(hào)Q1313135,非誠(chéng)勿擾.
總結(jié)
以上是生活随笔為你收集整理的动态规划之买瓜子—C说算法系列的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 域名抢注自动提交程序详解
- 下一篇: 【yoyo】移入切换
