思路
题目的思维量很高,参考大佬的题解
-
要想用的币最少,我们可以用贪心的思想,用的币值越大越好
-
因为大的币值一定是小的币值的倍数,所以一个大的币值一定可以代替i个相等小的币值,所以我们可以用小的币值的更新大的币值。
-
状态定义:
f[i] 表示在使用面值最大为i合法硬币序列时,我们用的最少钱币数量
-
状态转移,根据前面的分析可以得知,f[i] 可以用来更新大面值的结果,所以我们可以用
f[i]
更新f[i * j]
,f[i * j] = min(f[i * j], f[i] - cnt)
, cnt : 使用面值i * j 之后可以减少的钱币数量。 -
cnt 的计算方法,每个兔子减少:
-
最后取
min(f[i])
即可 -
注意点当
i * j > max_price
时无意义,为什么这么说呢,因为题目要求不能找零,所以大于max_price 的币花不出去。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int f[N], w[N];// f[i]表示在使用面值最大为i合法硬币序列时,我们用的最少钱币数量
int main()
{
memset(f, 0x3f, sizeof f);
f[1] = 0;
int n, max_price = -1;
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> w[i];
f[1] += w[i];
max_price = max(max_price, w[i]);
}
//cout << max_price << endl;
for(int i = 1; i <= max_price; i ++)
{
for(int j = 2; i * j <= max_price; j ++)
{
int cnt = 0;
for(int k = 1; k <= n; k ++)
{
cnt += w[k] / (i * j);
}
cnt = cnt * (j - 1);
f[i * j] = min(f[i * j], f[i] - cnt);
}
}
int res = 1e9 + 10;
for(int i = 1; i <= max_price; i ++)
{
res = min(res, f[i]);
}
cout << res << endl;
return 0;
}