最少硬币数的找零方案(动态规划)

题目描述:

给定需要找零的面值m和可以使用的硬币种类n(假设每种硬币有无限多个),求出用这些硬币找零m所需要的最少硬币数。

输入描述:

第一行包含两个整数:m ,n,意义如题目描述。接下来的 n 行,每行一个整数,第 i + 1 行的整数表示第 i 种硬币的面值。

输出描述:

输出一个整数,表示最少需要携带的硬币数量。如果无解,则输出-1。

分析:

我们用 d(i) = j 来表示找零 i 元最少需要 j 个硬币。于是我们得到 d(0) = 0, 表示找零0元最少需要0个硬币。

当 i = 1 时,只有面值为1的硬币可用,有:

d(1) = d(1 - 1) + 1 = d(0) + 1 = 1

当 i = 2 时,可用硬币的面值是1、2,有:

d(2) = min{ d(2 - 1) + 1, d(2 - 2) + 1 } = min{ d(1) + 1, d(0) + 1 } = 1

当 i = 3 时,可用硬币的面值是1、2,有:

d(3) = min{ d(3 - 1) + 1, d(3 - 2) + 1 } = min{ d(2) + 1, d(1) + 1 } = 2

当 i = 4 时,可用硬币的面值是1、2,有:

d(4) = min{ d(4 - 1) + 1, d(4 - 2) + 1 } = min{ d(3) + 1, d(2) + 1 } = 2

当 i = 5 时,可用硬币的面值是1、2、5,有:

d(5) = min{ d(5 - 1) + 1, d(5 - 2) + 1, d(5 - 5) + 1 } = min{ d(4) + 1, d(3) + 1, d(0) + 1 } = 1

……

显然用动态规划实现这一过程,代码如下。

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<vector>
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     int m = 0;            // 要找零的面值
 9     int n = 0;            // 不同面值的硬币种类
10     vector<int> coin;    // 不同面值的硬币
11     vector<int> status;    // status[i]表示找零面值i最少需要的硬币数
12     cin >> m >> n;
13     for (int i = 0; i < n; i++){
14         int temp;
15         cin >> temp;
16         coin.push_back(temp);
17     }
18     sort(coin.begin(), coin.end());        // 硬币按面值升序排列
19     reverse(coin.begin(), coin.end());    // 硬币按面值降序排列
20     status = vector<int>(m + 1, 0);        // 初始化为0
21     for (int i = 1; i <= m; i++)
22     {
23         vector<int> min;
24         for (auto it = coin.begin(); it != coin.end(); it++)
25         {
26             // 硬币恰好找零
27             if (i == *it)
28             {
29                 min.push_back(1);
30                 break;        // 跳出循环,结束寻找
31             }
32             // 硬币可以找开 并且 i - *it 有找零方案
33             else if (i > *it && status[i - *it] > 0)
34             {
35                 min.push_back(status[i - *it] + 1);    // 存入用*it找零需要的硬币数
36             }
37             // 硬币找不开
38             else
39                 continue;
40         }
41         if (min.size() > 0)
42             status[i] = *min_element(min.begin(), min.end());    // 找到最少的硬币数
43         else
44             status[i] = -1;        // 找不开
45     }
46     cout << *(status.end() - 1) << endl;                // 输出找零m需要的最少硬币数
47     //for (int i = 0; i < status.size(); i++)
48     //{
49     //    cout << i << "\t" << status[i] << endl;            // 输出找零i(0<=i<=m)需要的最少硬币数
50     //}
51     system("pause");
52     return 0;
53 }

注意33行的代码,双重判断:

else if (i > *it && status[i - *it] > 0)

第一个判断,确定 *it 可以找零,第二个判断,确定用 *it 找零后,i - * it 也能够找零。

测试:

1. 用3种硬币1、2、5找零20,需要4个硬币(4个5)。

2. 用3种硬币2、3、5找零11,需要3个硬币(5、3、3)。

 

 附:

如果想分别输出面值1~m的最少找零硬币数,并给出对应的找零方案呢?代码稍加修改即可,见下。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<vector>
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     int m = 0;            // 要找零的面值
 9     int n = 0;            // 不同面值的硬币种类
10     vector<int> coin;    // 不同面值的硬币
11     vector<int> status;    // status[i]表示找零面值i最少需要的硬币数
12     vector<vector<int>> trace;    // trace[i]表示找零面值i用到的硬币面值
13     const int maxInt = 0x7FFFFFFF;
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     reverse(coin.begin(), coin.end());    // 硬币按面值降序排列
22     status = vector<int>(m + 1, 0);        // 初始化为0
23     trace = vector<vector<int>>(m + 1);
24     for (int i = 1; i <= m; i++)
25     {
26         int min = maxInt;
27         for (auto it = coin.begin(); it != coin.end(); it++)
28         {
29             // 硬币恰好找零
30             if (i == *it)
31             {
32                 min = 1;
33                 trace[i].push_back(*it);
34                 break;        // 跳出循环,结束寻找
35             }
36             // 硬币可以找开
37             else if (i > *it && status[i - *it] > 0)
38             {
39                 if (min > status[i - *it] + 1)
40                 {
41                     min = status[i - *it] + 1;        // 存入用*it找零需要的硬币数
42                     trace[i].clear();
43                     for (auto &it1 : trace[i - *it])
44                         trace[i].push_back(it1);
45                     trace[i].push_back(*it);
46                 }
47             }
48             // 硬币找不开
49             else
50                 continue;
51         }
52         if (min == maxInt)
53             status[i] = -1;        // 找不开
54         else
55             status[i] = min;    // 更新最少的硬币数
56     }
57     //cout << *(status.end() - 1) << endl;                // 输出找零m需要的最少硬币数
58     for (int i = 1; i < status.size(); i++)
59     {
60         cout << i << "\t" << status[i] << "\t";                    // 输出找零i(1<=i<=m)需要的最少硬币数
61         for (auto &it : trace[i])                                // 输出找零i(1<=i<=m)需要最少硬币数的硬币组合
62             cout << it << "\t";
63         cout << endl;
64     }
65     system("pause");
66     return 0;
67 }
View Code

测试:

20 4

1

2

5

10

100 5

3

7

9

11

17

posted @ 2019-04-09 18:51  GarrettLu  阅读(1159)  评论(0编辑  收藏  举报