動態規劃演算法求解硬幣找零問題
阿新 • • 發佈:2019-02-14
硬幣找零問題描述:現存在一堆面值為 V1、V2、V3 … 個單位的硬幣,問最少需要多少個硬幣才能找出總值為 T 個單位的零錢?假設這一堆面值分別為 1、2、5、21、25 元,需要找出總值 T 為 63 元的零錢。
很明顯,只要拿出 3 個 21 元的硬幣就湊夠了 63 元了。
基於上述動態規劃的思想,我們可以從 T=1 開始計算出最少需要幾個硬幣,然後再求 T=2 、T=3…每一次求得的結果都儲存在一個數組中,以後需要用到時則直接取出即可。
從 T=1 開始依次找零時,可以嘗試一下當前要找零的面值 T 是否能夠被分解成 另一個已求解的面值的找零需要的硬幣個數 再加上 這一堆硬幣中的某個面值之和,如果這樣分解之後最終的硬幣數是最少的,那麼問題就得到答案了。
單是上面的文字描述太抽象,先假定以下變數:
values[] : 儲存每一種硬幣的幣值的陣列
valueKinds :幣值不同的硬幣種類數量,即values[]陣列的大小
money : 需要找零的面值
dp[] : 儲存面值為 i 的紙幣找零所需的最小硬幣數
演算法描述:
當求解總面值為 i 的找零最少硬幣數 dp[i] 時,將其分解成求解 dp[i – cents] 和一個面值為 cents 元的硬幣,由於 i – cents < i , 其解 dp[ i – cents] 已經在之前的迴圈中被算出,如果面值為 cents 的硬幣滿足題意,那麼最終解 dp[i] 則等於 dp[i – cents] 再加上 1(即面值為 cents)的這一個硬幣。
上面的程式碼並沒有給出具體應該是哪幾個面值的硬幣,這個可以再使用一些陣列儲存而打印出來。public class CoinsChange { /** * 硬幣找零:動態規劃演算法 * @param values:儲存每一種硬幣的幣值的陣列 * @param valueKinds:幣值不同的硬幣種類數量,即coinValue[]陣列的大小 * @param money:需要找零的面值 * @param dp:儲存面值為i的紙幣找零所需的最小硬幣數 */ public static void makeChange(int[] values, int valueKinds, int money, int[] dp) { dp[0] = 0; // 對每一分錢都找零,即儲存子問題的解以備用,即填表 for (int cents = 1; cents <= money; cents++) { // 當用最小幣值的硬幣找零時,所需硬幣數量最多 int minCoins = cents; // 遍歷每一種面值的硬幣,看是否可作為找零的其中之一 for (int kind = 0; kind < valueKinds; kind++) { // 若當前面值的硬幣小於當前的cents則分解問題並查表 if (values[kind] <= cents) { int temp = dp[cents - values[kind]] + 1; if (temp < minCoins) { minCoins = temp; } } } // 儲存最小硬幣數 dp[cents] = minCoins; System.out.println("面值為 " + (cents) + " 的最小硬幣數 : " + dp[cents]); } } public static void main(String[] args) { // 硬幣面值預先已經按降序排列 int[] coinValue = new int[] { 25, 21, 10, 5, 1 }; // 需要找零的面值 int money = 63; // 儲存每一個面值找零所需的最小硬幣數,0號單元捨棄不用,所以要多加1 int[] dp = new int[money + 1]; makeChange(coinValue, coinValue.length, money, dp); } }