[動態規划]最少硬幣問題


最少硬幣問題

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的最少硬幣數。

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


算法思路:

  1. 這題采用"動態規划"算法,將大問題轉換為小問題,且一步步記錄上一步的結果。
  2. 我們依舊使用一維數組來存儲計算結果,可以參考之前的一個"背包問題"的算法解讀:https://www.cnblogs.com/onetrainee/p/11672203.html

與"背包問題"的對比與分析:

  1. “背包”中給出了"損失"與"收益",但其都是對於單個物品,求最大收益;“硬幣”中給出的是"收益"、"個數"與"最終收益",求最小組合。
  2. 可以看出,如果對於"硬幣問題",要采用動態規划的話,依照"一步步走的原則",至少要進行拆分,比如 5 個 2,要拆分成 5 、5,先計算第一個5,之后再計算第二個5。

算法設計:

  1. 依舊采用一維數組 "dp[目標面額]=最小需求個數" 來存儲最終結果。
  2. 我們將所有硬幣依次拆分成單個,dp[目標面額] = min(dp[目標面額-當前面額]+1,dp[目標面額]),前一個是將該面額放入該組合中,后一個是不采用當前面額,選取數值最小的。

算法注意事項:

  1. 前兩層循環是將面額拆分,注意邊界是否帶等號。
  2. 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             

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM