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