题目:有n种硬币,面值分别为V1,V2,…Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值!
若用记忆化搜索,需构建2个dp函数,如下为只写了求最大数量的代码
#include
#include
#include
using namespace std;
int d[100]; //表示d[S],凑齐S需要最多的硬币数
int n=3; //硬币有3种种类
int v[4] = { 1,3,5 };
int dp(int s)
{
int i;
int& ans = d[s]; //重新引用ans,是当d[i]换成d[i][j][k]时,该技巧的优势就会出来
if (ans != -1) {
return ans;
}
//ans = -(1 << 30); //位移运算,在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。
ans = -10;
for (i = 0; i < n; i++) {
if (s >= v[i]) {
ans = max(ans, dp(s - v[i]) + 1);
}
}
return ans;
}
int main()
{
int i, sum, ans = -1;
memset(d, -1, sizeof(d));
d[0] = 0; //若结束,为0,如果不设置会出错
cout << "请输入想要计算的金额 ";
cin >> sum; //想要凑齐的金额
for (i = 0; i < n; i++) {
ans = max(ans, dp(sum - v[i]) + 1);
}
cout << "需要的最多的硬币数为 " << ans;
return 0;
}
如果想计算最大和最小,用递推比较方便,先计算金额1需要多少个,再计算2需要多少,一直到s,且不会重复计算
#define INF 0x3f3f3f3f
#include
#include
#include
using namespace std;
int minv[100],maxv[100]; //表示d[S],凑齐S需要最多的硬币数
int n=3; //硬币有3种种类
int v[4] = { 1,3,5 };
void print_ans(int *d,int s)
{
int i;
for (i = 0; i < n; i++) {
if (s >= v[i] && d[s] == d[s-v[i]] + 1) {
cout << v[i] << " ";
print_ans(d, s - v[i]);
break;
}
}
}
int main()
{
int i,j,sum;
memset(minv, INF, sizeof(minv)); //最开始要设置成很大的数
memset(maxv, -INF, sizeof(maxv));
minv[0] = maxv[0] = 0; //若结束,为0,如果不设置会出错
cout << "请输入想要计算的金额 ";
cin >> sum; //想要凑齐的金额
for (i = 1; i <= sum; i++) {
for (j = 0; j < n; j++) {
if (i >= v[j]) {
minv[i] = min(minv[i], minv[i - v[j]] + 1);
maxv[i] = max(maxv[i], maxv[i - v[j]] + 1);
}
}
}
cout << "需要的最多的硬币数为 " << maxv[sum]<
还有一种用空间代替时间的例子,用min_coin和max_coin消除了原来print_ans的循环
#define INF 0x3f3f3f3f
#include
#include
#include
using namespace std;
int minv[100],maxv[100]; //表示d[S],凑齐S需要最多的硬币数
int n=3; //硬币有3种种类
int v[4] = { 1,3,5 };
void print_ans(int *d,int s)
{
while (s) {
cout << d[s]<<" ";
s = s - d[s];
}
}
int main()
{
int i,j,sum;
int min_coin[100], max_coin[100]; //用空间代替时间
memset(minv, INF, sizeof(minv)); //最开始要设置成很大的数
memset(maxv, -INF, sizeof(maxv));
minv[0] = maxv[0] = 0; //若结束,为0,如果不设置会出错
cout << "请输入想要计算的金额 ";
cin >> sum; //想要凑齐的金额
for (i = 1; i <= sum; i++) {
for (j = 0; j < n; j++) {
if (i >= v[j]) {
if (minv[i] > minv[i - v[j]] + 1) {
minv[i] = min(minv[i], minv[i - v[j]] + 1);
min_coin[i] = v[j];
}
if (maxv[i] < maxv[i - v[j]] + 1) {
maxv[i] = max(maxv[i], maxv[i - v[j]] + 1);
max_coin[i] = v[j];
}
}
}
}
cout << "需要的最多的硬币数为 " << maxv[sum]<
结果