( ext{C - 1, 2, 3 - Decomposition})
解法
定义一个数为好数当且仅当它由 (1,2,3) 组成,(f(i)) 就是 (i) 的答案。
分而治之 的思想挺妙的,但我压根没想到。考虑将 (n) 分解成 (10p+r) 的形式(并非除数与余数),可以想到,(f (p)) 分解的每个好数再乘以 (10) 加上 (1,2,3) 就又是一个好数。
基于此,不妨令 (kle rle 3k),那么如果 (f(p)le k) 就意味着一定能找出解,即有 (1,2,3) 与每个好数匹配。注意不满足条件也不意味着一定无解,这点下文详细再谈。
考虑时间复杂度。
打表发现,(f(n)le 5)。现在我们加以证明:转化一下,现在只用证明 (n) 一定能被分解成这样的 (p,r) 满足 (f(p)le 5) 且 (5le rle 15)。我们可以归纳证明。首先当 (ile 15) 时一定有 (f(i)le 5),对于其它情况,你发现 (r) 的个位取值取到了 ([0,9]) 中每个数字!而我们又可以任意减去 (10) 的倍数,所以结论得证。
既然我们证明这样构造一定有解,就不用考虑不满足构造仍有解的情况了。
由于 (f(n)le 5) 且 (5le rle 15),我们容易得出对于每个 (n),(p) 至多有两种取值,而迭代次数由于每次除以 (10) 非常小。所以这是可过的。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <map>
using namespace std;
typedef long long ll;
map <ll,int> mp;
int f(ll n) {
if(mp.count(n)) return mp[n];
int ans=-1;
for(ll k=1;k<=n;++k) {
for(ll i=k;i<=k*3;++i) {
if(n-i<0) break;
if((n-i)%10==0) {
if(f((n-i)/10)<=k) {
ans=k; break;
}
}
}
if(~ans) break;
}
return mp[n]=ans;
}
int main() {
mp[0]=0;
for(int T=read(9);T;--T) {
ll n=read(9ll);
print(f(n),'
');
}
return 0;
}