1、题目描述:给定数组arr,,大小为N,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。
举例:arr=[5,2,3],aim=20,返回4;arr=[5,2,3],aim=0,返回0;arr=[3,5],aim = 2,返回-1。
2、题目解析:
1)组合数学中的母函数一节可以很好的解决这个问题;
2)从动态规划的角度来看,首先要确定动态规划表dp,在这里dp[i][j]的含义是:使用任意的a[0]...a[i],组合成j的最小货币数,因此动态规划表的大小为N行,aim+1列。具体为什么这么构造dp表,我也不清楚,准备多做一些类似的题目看一看。
3)接下来的关键就是构造dp[i][j]的表达式了,分成使用a[i]和不使用a[i]的情况(也可以根据其他的货币来分类,不过用a[i]分类容易)。不使用a[i]的话,dp[i][j]=dp[i-1][j],也就是使用a[0]...a[i-1]就能够组合出了aim了;使用a[i]的话,dp[i][j]=dp[i][ j-arr[i] ]+1(确保j-arr[i]>=0),也就是使用任意张a[0]...a[i]组合出j-arr[i],最后加上a[i](为什么是这样,我也不清楚)。
3、最后附上代码:
/*2017-11-20
* 换钱的最少货币数:给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数
* 求组成aim的最少货币数
*/
#include<iostream>
#include<vector>
using namespace std;
int min(int v1, int v2);
int main() {
int N, aim;
int max = 65536;
cout << "please input N and aim:" << endl;
cin >> N >> aim;//输入货币面值的种类数和目标钱数
vector<int> arr(N);
vector<vector<int>> dp(N, vector<int>(aim + 1));
//输入各种面值
cout << "please input arr:" << endl;
for (auto it_arr = arr.begin(); it_arr != arr.end(); ++it_arr) {
cin >> *it_arr;
}
dp[0][0] = 0;
//对dp的第一行进行初始化
for (int i = 1; i < aim + 1; ++i) {
if (i%arr[0] == 0) {
dp[0][i] = i / arr[0];
}
else {
dp[0][i] = max;//max代表无法组成这个aim
}
}
//对dp第一列进行初始化,全为0
for (int j = 1; j < N; j++) {
dp[j][0] = 0;
}
//计算dp[i][j],最终返回dp[N-1][aim]
for (int i = 1; i < N; ++i) {
for (int j = 1; j < aim + 1; ++j) {
if (arr[i] > j) {
dp[i][j] = dp[i - 1][j];
}
else {
dp[i][j] = min(dp[i - 1][j], dp[i][j - arr[i]] + 1);
}
}
}
if (dp[N - 1][aim] == 65536)dp[N - 1][aim] = -1;
//输出结果
cout << "the minimum number is: " << dp[N - 1][aim] << endl;
}
int min(int v1, int v2) {
if (v1 < v2) return v1;
else return v2;
}