题目是问100块钱用现有的币值,有多少组成方式。
要注意的是,一开始用float,发现 5 / 0.1算出来是49.999多,原来浮点数是有误差的,所以大家都*10,用整数来除。
方法一:用递归
#include <iostream>
using namespace std;
int num[8];
int money[8] = {1, 5, 10, 50, 100, 200, 500, 1000};
int nums = 0;
float total;
void f(int index, float current)
{
if (index >= 8)
return;
if (current > total)
return;
if (index == 7 && current == total)
{
nums++;
for (int i = 0; i < 8; i++)
{
cout << float(float(money[i])/10) << " : " << num[i] << ", ";
}
cout << endl;
}
for (int i = 0; i <= (total-current) / money[index]; i++)
{
num[index] = i;
f(index + 1, current + money[index] * num[index]);
}
}
int main()
{
cin >> total;
total = total * 10;
for (int i = 0; i < 8; i ++)
{
num[i] = 0;
}
int current = 0;
f(0, current);
cout << nums << endl;
nums = 0;
}
但是,用递归算法效率差了点,想想是否可以用DP。
方法二:用DP
效率变快好多。
f[k] 记录组成k元有多少组成方式。
当遍历到money[i]时,f[k]记录的是用money[0] - money[i-1]的币值可以组成k元的组成方式的个数。
那么到money[i]时,观察f[k],如果f[k] > 0,代表之前可以组成k元,然后加入j个money[i] 就可以组成k+money[i] * j 元。
且k+money[i] * j 元的组成方式的个数要加上f[k]。
用数组g代表当前money[i]时的情况,否则新产生的值会和上一轮的f的值混淆。到这一轮结束以后,再把g的值加到f的值之上即可。
想起来这道题好像以前做过类似的:)
#include <iostream>
using namespace std;
int money[8] = {1, 5, 10, 50, 100, 200, 500, 1000};
int main()
{
int N;
cin >> N;
while(N-- > 0)
{
int total;
cin >> total;
total = total * 10;
long long *f = new long long[total+1];
long long *g = new long long[total+1];
for (int i = 0; i <= total; i ++)
{
f[i] = 0;
g[i] = 0;
}
for (int j = 1; j <= total / money[0]; j++)
{
f[money[0] * j]++;
}
for (int i = 1; i < 8; i++)
{
for (int j = 1; j <= total/ money[i]; j++)
{
g[money[i] * j]++;
}
for (int k = 0; k <= total; k++)
{
if (f[k] > 0)
{
for (int j = 1; j <= (total - k) / money[i]; j++)
g[k + money[i] * j] += f[k];
}
}
for (int k = 0; k <= total; k++)
{
f[k] += g[k];
g[k] = 0;
}
}
cout << f[total] << endl;
}
}