數學基礎:三、動態規劃2(求解湊齊錢的最小張數)

湊齊錢的最小張數概念:

比如只有2塊、3塊和5塊錢若干,問湊齊100塊錢最小需要幾張錢能湊齊?(20張5塊的,所以是20張)

前面一篇求解編輯距離時,有現成的狀態轉移方程,可是這種湊齊面值的沒有現成的公式,只能自己去推導。

當然我們可以利用求餘數求解,湊齊98塊,需要98/5=19…3,所以爲19張5塊+1張3塊(一共20張)

但這個可能用餘數可能更方便,但對於動態歸劃方法,可能是個思路

比如只有2、3、7塊,用表格推導的過程如下:
在這裏插入圖片描述
而如果只有2、5、9塊,它的推導如下:
在這裏插入圖片描述
根據推導過程,寫出的算法如下:

public class Lesson10_1 {
    // 錢幣幣種可選的值,自己可隨意改
    private static final int[] coins = {2, 5, 9};

    // 如果不加變量保存,每次都要重複遞歸計算
    private static Map<Integer, Integer> hasCount = new HashMap<>();

    // 傳入總金額和可選幣種,返回湊夠該金額的最小數量,返回null表示沒有找到
    // 爲null表示湊不齊這個錢(商場買菜要1塊錢,可選幣種最小值要2塊,我們不能傻得給人家2塊不找零,此時就是湊不齊)
    public static Integer getCount(int total, int[] coins) {
        // 如果之前已計算過該金額的,直接返回,無需遞歸調用了
        if (hasCount.containsKey(total)) {
            return hasCount.get(total);
        }
        // 遞歸減到了負數,也沒湊齊,就是沒找到
        if (total < 0) {
            return null;
        } else if (total == 0) {// 金額爲0,那需要的數量肯定爲0
            return 0;
        } else {
            int length = coins.length;
            // 保存可選幣種中,每種所需的最小數量
            Integer[] min = new Integer[length];
            for (int i = 0; i < length; i++) {
                // 比如求湊20塊錢,可選值是5塊錢,那遞歸求湊15塊+1即可    C20(5) = C15(5) + 1
                // 再比如求湊18塊錢,可選值2塊錢,那遞歸求湊16塊+1即可    C18(2) = C16(2) + 1
                // C n (m) = C n-m (m) + 1
                Integer tmpResult = getCount(total - coins[i], coins);
                // 返回值爲空,說明遞歸調用到 if (total < 0) 時返回了空,沒有湊夠該值
                min[i] = tmpResult != null ? (tmpResult + 1) : null;
            }
            // 去除空值
            List<Integer> resultList = Arrays.stream(min).filter(e -> e != null).collect(Collectors.toList());
            // 如果不爲空,那最小值就是所需的最小個數
            Integer result = resultList.size() > 0 ? resultList.stream().mapToInt(e -> e).min().getAsInt() : null;
            // 放到map中,防止後續重複遞歸計算
            hasCount.put(total, result);
            return result;
        }
    }

    public static void main(String[] args) {
        Integer count = getCount(102, coins);
        System.out.println(count);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章