最少硬幣問題
http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/3016/pid/1725
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
設有n種不同面值的硬幣,各硬幣的面值存於數組T[1:n]中。現要用這些面值的硬幣來找錢。可以使用的各種面值的硬幣個數存於數組Coins[1:n]中。
對任意錢數0≤m≤20001,設計一個用最少硬幣找錢m的方法。
對於給定的1≤n≤10,硬幣面值數組T和可以使用的各種面值的硬幣個數數組Coins,以及錢數m,0≤m≤20001,計算找錢m的最少硬幣數。
對任意錢數0≤m≤20001,設計一個用最少硬幣找錢m的方法。
對於給定的1≤n≤10,硬幣面值數組T和可以使用的各種面值的硬幣個數數組Coins,以及錢數m,0≤m≤20001,計算找錢m的最少硬幣數。
Input
輸入數據第一行中只有1個整數給出n的值,第2行起每行2個數,分別是T[j]和Coins[j]。最后1行是要找的錢數m。
Output
輸出數據只有一個整數,表示計算出的最少硬幣數。問題無解時輸出-1。
Sample Input
3 1 3 2 3 5 3 18
Sample Output
5
Hint
Source
算法思路:
- 這題采用"動態規划"算法,將大問題轉換為小問題,且一步步記錄上一步的結果。
- 我們依舊使用一維數組來存儲計算結果,可以參考之前的一個"背包問題"的算法解讀:https://www.cnblogs.com/onetrainee/p/11672203.html
與"背包問題"的對比與分析:
- “背包”中給出了"損失"與"收益",但其都是對於單個物品,求最大收益;“硬幣”中給出的是"收益"、"個數"與"最終收益",求最小組合。
- 可以看出,如果對於"硬幣問題",要采用動態規划的話,依照"一步步走的原則",至少要進行拆分,比如 5 個 2,要拆分成 5 、5,先計算第一個5,之后再計算第二個5。
算法設計:
- 依舊采用一維數組 "dp[目標面額]=最小需求個數" 來存儲最終結果。
- 我們將所有硬幣依次拆分成單個,dp[目標面額] = min(dp[目標面額-當前面額]+1,dp[目標面額]),前一個是將該面額放入該組合中,后一個是不采用當前面額,選取數值最小的。
算法注意事項:
- 前兩層循環是將面額拆分,注意邊界是否帶等號。
- dp[k]數組初始化時,除k=0外所有數值設置為無窮大,因為當需求為0時,其組合數本來就為0種,如果這個也設置為無窮大,則無法計算了。
源代碼:
1 // 算法.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。 2 // 3 4 #include "pch.h" 5 #include <iostream> 6 #include <algorithm> 7 #define INF 0x3f3f3f3f 8 using namespace std; 9 10 int main() { 11 12 int des, n, T[20001], Coin[20001]; 13 long long int dp[20001]; 14 memset(dp, INF, sizeof(dp)); 15 cin >> n; 16 for (int i = 0; i < n; i++) { 17 cin >> T[i] >> Coin[i]; 18 } 19 20 cin >> des; 21 dp[0] = 0; 22 23 // 算法注意邊界是否帶等號 24 for (int i = 0; i < n; i++) 25 for (int j = 1; j <= Coin[i]; j++) 26 for (int k = des; k >= T[i]; k--) 27 dp[k] = min(dp[k], dp[k - T[i]] + 1); 28 29 cout << (dp[des]<INF?dp[des]:-1) << endl; 30 } 31