题意
一个货币系统要求一共有 m 种货币,并且将它们按照币值从小到大排好序以后,前一个货币币值乘上 x 等于后一个货币币值,\(x ∈ {2, 3, 4, 5}\),且最小的币值一定为 1。
请设计一个货币系统,使得它表示总币值为 n 的钱所需的货币总张数最少。
\(n ≤ 10^18\)。
\(m ≤ 100\)。
Source : BZOJ 4265
题解
下一个货币照顾不到前一个货币能照顾的情况。
假如说x只有一个,就是x进制,其实x有四个跟一个没有区别的。
最终可以把n写成这样的一个式子
\[n=1*(k_1*(a_1+(k_2*(a_2+...)))) \]最终的答案数应该是\(a_1+a_2+\dots+a_n\)。
只要按照进制的算法,每次除\(k_i\)取余求一个最小值就行了。
注意这里n比较大,但是实际上用到的状态数其实很小,可以用map代替数组。
时间复杂度\(O(mlog^3n)\)。
#include <bits/stdc++.h>
#define int long long
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
char c; int num, f = 1;
while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
return f * num;
}
int n, m;
map<int,int> f[105];
int dfs(int x, int y) {
if(f[x].count(y)) return f[x][y];
if(y == 0) return 0;
if(x == 1) return y;
int minn = 1e18;
for(int i = 2; i <= 5; i++)
minn = min(minn, dfs(x - 1, y / i) + y % i);
f[x][y] = minn;
return f[x][y];
}
signed main()
{
n = read(); m = read();
printf("%lld\n", dfs(m, n));
return 0;
}