最少的硬币数量组合出1到m之间的任意面值(贪心算法)
题目描述:
你有n种不同面值的硬币,每种面值的硬币都有无限多个,为了方便购物,你希望带尽量少的硬币,并且要能组合出 1 到 m 之间(包含1和m)的所有面值。
输入描述:
第一行包含两个整数:m ,n(1 ≤ n ≤ 100,1 ≤ m ≤ 109),意义如题目描述。接下来的 n 行,每行一个整数,第 i + 1 行的整数表示第 i 种硬币的面值。
输出描述:
输出一个整数,表示最少需要携带的硬币数量,再输出以空格为分隔的一串整数,表示对应的硬币面值。如果无解,则输出-1。
示例:
输入:
20 4
1
2
5
10
输出:
5
1 2 2 5 10
分析:
首先,硬币面值必须有1,否则无法组合1(其实只要有了1,便可以组合出任意面值)。
用sum表示当前能组合的最大面值,即可以组合1~sum的所有面值。
当sum ≥ m时,就停止组合。
当sum < m时,要继续组合,即组合sum + 1,我们事先把不同面值的硬币排序,从这里面找到满足 ≤ sum + 1的最大面值的硬币,为什么需要是最大面值呢,因为这样才能保证硬币数最少。假设找到的硬币面值是coin[i],此时,更新sum += a[i],并将硬币数量+1。以上面的示例为例,当sum = 4时,下一次要凑5,我们在面值数组里找到了满足条件的5,因为sum = 4表示能凑齐1~4,现在有了面值为5的硬币,又可以组合出5~9,总共可以组合出1~9,所以我们更新sum = 4 + 5 = 9,表示现在可以组合1~9。按此思想,如此循环下去……,直到sum ≥ m。
显然是用贪心算法来解决问题了。
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 6 int main() 7 { 8 int m = 0; // 要组合出1~m的任意面值 9 int n = 0; // 不同面值的硬币数量 10 vector<int> coin; // 不同面值的硬币 11 int sum = 0; // 当前能组合出的最大面值 12 int count = 0; // 当前需要的硬币数量 13 vector<int> trace; // 当前需要的硬币面值 14 cin >> m >> n; 15 for (int i = 0; i < n; i++){ 16 int temp; 17 cin >> temp; 18 coin.push_back(temp); 19 } 20 sort(coin.begin(), coin.end()); //硬币按面值升序排列 21 // 如果没有面值为1的硬币,则无解 22 if (coin[0] != 1){ 23 cout << -1 << endl; 24 system("pause"); 25 return 0; 26 } 27 while (true){ 28 // 如果可以组合出大于等于m的面值,则输出count 29 if (sum >= m){ 30 cout << count << endl; 31 for (auto &i : trace) 32 cout << i << "\t"; 33 system("pause"); 34 return 0; 35 } 36 // 找满足<= sum + 1的最大面值的硬币 37 for (int i = n - 1; i >= 0; i--){ 38 if (coin[i] <= sum + 1){ 39 trace.push_back(coin[i]); // 保存需要的硬币面值 40 sum += coin[i]; // 更新sum 41 count++; // 更新count 42 break; // 跳出并判断此时的sum是否>=m 43 } 44 } 45 } 46 return 0; 47 }
测试: