湊齊錢的最小張數概念:
比如只有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);
}
}