问题很常见吧!就是给定一个钱数n,然后给定一个k种硬币
然后输入硬币的面值,然后求出最少的硬币来凑出这个钱数
例如:11块
有三种硬币 1 2 5
那么最少数就是 3(两个5和一个1)
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
int dp(int n,vector<int> &coin,vector<int> &memo);
int main(){
int money;
cin >> money;
//硬币的种类
int k;
cin >> k;
//定义一个k大小的coin容器,存储硬币的面值
vector<int> coin;
for(int i=0;i<k;i++){
int x;
cin >> x;
coin.push_back(x);
}
//定义一个备忘录 ,初始化大小为钱数+1,初始值为0代表没有存储数据
vector<int> memo(money+1,0); //易错点memo的大小不要越界
cout << dp(money,coin,memo);
return 0;
}
int dp(int n,vector<int> &coin,vector<int> &memo){
//当n为0的时候需要0个硬币
if(n==0) return 0;
//当n小于0此时无解
if(n<0) return -1;
//!!!注意这里很重要,我们要非常注意这个访问备忘录要确认n一定要大于零,不然会访问越界失败。
if(memo[n]!=0) return memo[n];
//定义最优解的值为无限大
int ans = 10000000;
//求解当前钱数需要硬币的最小数
for(int i=0;i<coin.size();i++){
//每次用一种硬币去试,也就是子问题
int subproblem;
subproblem = dp(n-coin[i],coin,memo);
// 此时子问题无解,跳过
if(subproblem == -1) continue;
ans = min(ans,subproblem+1);
}
// 把最优解存入备忘录
memo[n] = ans;
return memo[n];
}
这里有两点需要注意的:(也是笔者犯错的两个地方):
- 一个就是我们设置备忘录的时候大小要确定好,比如这里我们的备忘录大小需要是n+1,不然我们访问memp[n]的时候就会越界导致错误
- 另外一个就是,我们的子问题访问备忘录的时候一定要确定n>0,不然也会越界。也就是把访问备忘录放在两个边界情况后。这里如果我们memo设置为0的话不会出错但是如果题目需要设置为其他值的话就会导致越界。
不太能理解的话,就画出递归树,递归问题画图更容易帮助我们理解。
不懂的话可以评论区问我这个小菜鸡