javascript
java动态规划凑硬币问题,详解动态规划最少硬币找零问题--JavaScript实现
硬幣找零問題是動態(tài)規(guī)劃的一個經(jīng)典問題,其中最少硬幣找零是一個變種,本篇將參照上一篇01背包問題的解題思路,來詳細(xì)講解一下最少硬幣找零問題。如果你需要查看上一篇,可以點擊下面鏈接:
詳解動態(tài)規(guī)劃01背包問題--JavaScript實現(xiàn)
下面讓我們開始吧。
問題
給定4種面額的硬幣1分,2分,5分,6分,如果要找11分的零錢,怎么做才能使得找的硬幣數(shù)量總和最少。
分析
最少硬幣找零問題,是為了求硬幣的組合,所以一個大前提是硬幣無限量供應(yīng)。我們建立如下表格來分析問題:
其中每列用j表示零錢總額,每行i表示硬幣面額。T[i][j]表示硬幣個數(shù),它是我們即將填入表格的數(shù)字。
在填寫表格之前,我們需要先明確幾個規(guī)則:
當(dāng)填寫第i行時,使用的硬幣面額僅能是i以及小于i的面額。舉個例子,比如我填寫第0行,i=0,那么這一樣只能使用面額為1分的硬幣。當(dāng)我填寫第2行,i=2,那么可以使用1分,2分,5分三種面額的硬幣。
當(dāng)填寫第j列時,表示當(dāng)前需要使用硬幣湊出的總額。比如j=6,表示需要使用硬幣組合出總額為6分的情況。
1. i = 0
當(dāng)我們只能使用面額為1分的硬幣時,根據(jù)上面的規(guī)則,那么很顯然,總額為幾分,就需要幾個硬幣。即T[i][j] = j。
2. i = 1
當(dāng)我們有1分和2分兩種面額時,那么組合方式就相對多了點。
i=1 j = 1:總額為1時,只能使用1分的面額。即填1。
i=1 j = 2:總額為2時,可以使用2個1分的,也可以使用1個2分的。因為我們要求最少硬幣,所以使用1個2分的。表格所表達(dá)的意思是硬幣的數(shù)量,所以這里也填1。
i=1 j = 3:總額為3時,可以使用3個1分的,也可以使用1個1分加1個2分。因此這里應(yīng)該填2。
i=1 j = 4:總額為4時,可以使用4個1分的,可以使用2個1分加1個2分,也可以使用2個2分。其中硬幣最少的情況應(yīng)該是2個2分。因此這里填2。
i=1 j = 5:總額為5時,組合就更多了,但是聰明的你應(yīng)該能想到使用2個2分加1個1分,可以實現(xiàn)最少硬幣的需求。因此這里填3。
我們來看填寫完上面5格后的情況:
建議你自己再紙上照著我這圖畫一個表格。接下來,別急著填表。我們要根據(jù)已有的數(shù)據(jù),總結(jié)出T[i][j]的規(guī)律,然后通過填寫剩余表格來驗證。
我們將硬幣面額使用數(shù)組coins[i]來表示,根據(jù)表格有 1分=coins[0], 2分=coins[1]。
當(dāng)j
當(dāng)j>=coins[i]時,根據(jù)已有的 i=1行可以推出一個規(guī)律,令a = 1+T[i][j-coins[i]],T[i][j]= min(T[i-1][j],a),即二者比較取最小值。可能一開始你看到這個關(guān)于a的公式,有點太突然,難以接受。稍微解釋一下,當(dāng)?shù)趇行,優(yōu)先選擇這一樣的硬幣,因為這一行的硬幣面額最大,最有可能使得總硬幣數(shù)量最少。因此j-coins[i],就很好理解了,就是選擇了這一行的硬幣后,還剩下多少總額。舉個例子,當(dāng)i=1,j=3時,j-coins[1]=1。那么選擇2分后,還剩余總額為1,這時候我們再定位到i=1,j=1,即T[1][1],它的值為1,再加上一個常數(shù)1,即得最終結(jié)果2。
再舉例,i=1 j=5。由于是從左到右填表的,所以i=1,j<5的表格都填完了。j-coins[i]=3,定位到T[1][3]=2,加上常數(shù)1,即得最后結(jié)果T[1][5]=3。
其實公式本身很短,也很好記。如果實在無法理解,建議先不用糾結(jié)。先最小化瀏覽器,不要看本篇剩余的內(nèi)容。帶著這個解題公式,自己在紙上,把這個表格填寫完整,在填表分析的過程中就能慢慢理解了。
3. 剩余內(nèi)容
按照上一步所提供的公式,其實所有的T[i][j]都可以填完了。如下表格。
建議先自己再紙上填表,填完了,再和我的圖對比一下,看是否答案存在出入。
4.偽代碼
以上的填表邏輯,使用偽代碼表示如下
if(i == 0){
T[i][j] = j/coins[i]; //硬幣找零一定要有個 最小面額1,否則會無解
}else{
if(j >= coins[i]){
T[i][j] = min(T[i-1][j],1+T[i][j-coins[i]])
}else{
T[i][j] = T[i-1][j];
}
}
復(fù)制代碼
5. 尋找組合
至此,填完表格我們已經(jīng)接近完成了。接下來要尋找從表格中尋找硬幣組合。🤔
與填表順序相反,尋找組合從有下角開始。
首先需要明確的是如果T[i][j] == T[i-1][j],那么就向上搜索。根據(jù)圖來分析:
1. 定位到T[3][11] ,由于不存在T[i][j] == T[i-1][j],所以不用向上搜索,確定選中一個6分硬幣。尋找組合的思路和填寫T[i][j]的思路幾乎是反過來的。
2. 選擇一個6分硬幣后,剩余的總額為11-6=5。因此定位到T[3][5]中。由于T[3][5]==T[2][5],因此看圖中的藍(lán)色箭頭,向上搜索,直到T[i][j] != T[i-1]。
3. 定位到T[2][5]中,此時coins[i]為5分。選中5分硬幣只有,剩余的總額為5-5=0。
4. 當(dāng)j=0時,搜索結(jié)束。由上面步驟確定選中的硬幣組合為:1個5分,1個6分。
代碼
以上就是整個最少硬幣找零問題的分析思路。最終代碼使用 JavaScript 實現(xiàn),如果你的 Sublime 支持純 JavaScript,你可以直接復(fù)制黏貼代碼,command + b 直接運行查看結(jié)果,然后修改輸入變量,查看更多情況下的輸出結(jié)果。
//動態(tài)規(guī)劃 -- 硬幣找零問題
function minCoins(coins,total,n){
var T = [];
for(let i = 0;i
T[i] = []
for (let j=0;j<= total;j++){
if(j == 0){
T[i][j] = 0;
continue;
}
if(i == 0){
T[i][j] = j/coins[i]; //硬幣找零一定要有個 最小面額1,否則會無解
}else{
if(j >= coins[i]){
T[i][j] = Math.min(T[i-1][j],1+T[i][j-coins[i]])
}else{
T[i][j] = T[i-1][j];
}
}
}
}
findValue(coins,total,n,T);
return T;
}
function findValue(coins,total,n,T){
var i = n-1, j = total;
while(i>0 && j >0){
if(T[i][j]!=T[i-1][j]){
//鎖定位置,確定i,j值,開始找構(gòu)成結(jié)果的硬幣組合。 其實根據(jù)這種計算方法,只需要考慮最右邊那一列,從下往上推。
//console.log(T[i][j]);
break
}else{
i--;
}
}
var s = []; //存儲組合結(jié)果
while(i >= 0 && j > 0 ){
s.push(coins[i]);
j=j-coins[i];
if(j <= 0){
break; //計算結(jié)束,退出循環(huán)
}
//如果 i == 0,那么就在第 0 行一直循環(huán)計算,直到 j=0即可
if(i>0){
//console.log(i);
while(T[i][j] == T[i-1][j]){
i--;
if(i== 0){
break;
}
}
}
}
console.log(s);
//可以把數(shù)組s return 回去
}
var coins = [1,2,5,6];
var total = 11
var n = coins.length
console.log(minCoins(coins,total,n));
復(fù)制代碼
總結(jié)
以上是生活随笔為你收集整理的java动态规划凑硬币问题,详解动态规划最少硬币找零问题--JavaScript实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php中mysql,PHP中的mysql
- 下一篇: 紧致差分的matlab程序,对流占优扩散