题目描述
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.
【要求】
时间复杂度O(n \times aim)O(n×aim),空间复杂度On。
示例1
输入
复制
[5,2,3],20
返回值
4
示例2
输入
[5,2,3],0
返回值
0
示例3
输入
[3,5],2
返回值
-1
法一:递归(有问题)5 4 2 1,aim=8的话,求出来是3(5 ,2 ,1),而不是2(4,4)。
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,aim,sum=0,a[1005];
int dfs(int aim)
{
if(aim==0)return sum;
else
{
for(int i=n-1;i>=0;i--)
{
if(aim-a[i]>=0)
{
sum++;
//printf("现在从%d开始 %d\n",aim-a[i],sum);
if(dfs(aim-a[i])!=-1)return sum;
sum--;
}
}
}
return -1;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
scanf("%d",&aim);
if(dfs(aim)!=-1)
printf("%d",sum);
else
printf("-1");
return 0;
}
法一改进:
法二:这题是完全背包问题的变体,直接用动态规划。
定义dp[i][j]表示用arr[0:i-1]的元素能够刚好填满j的容量时的最小元素个数(钱个数),那么就有递推关系
dp[j] = min(dp[j], dp[j - arr[i]] + 1)
因此,有以下代码,O(n*aim) time and O(n) space:
int minMoney(vector<int>& arr, int aim) {
// write code here
int n = arr.size();
vector<long> dp(aim + 1, INT_MAX);
dp[0] = 0;
for (int j = 1; j <= aim; j++) {
for (int i = 0; i < n; i++) {
if (j >= arr[i]) dp[j] = min(dp[j], dp[j - arr[i]] + 1);
}
}
if (dp[aim] >= INT_MAX) return -1;
return dp[aim];
}