简要题意:
给一个十进制数,每次可以选择它的一个数码,然后它的值减去你选择的这个数码。
重复这个操作,直到这个数变成0,问最少需要多少次操作。
脑子卡住是什么感觉。
就是发现了正解要用的性质,不知道为什么就是没有继续往下细想。
啊艹为什么
题解:
首先容易注意到答案是单调不减的。
要证明这一点可以归纳,考虑 和 假设之前的一直都是单调,容易注意到 的最大数码不可能比 大超过 ,那么显然 能够到达的最小的数一定不会比 小,而前面的数全部满足单调性,所以 和 处满足单调性。
于是非常显然我们需要贪心减掉最大的数码。
考虑模拟这个过程,可以拿到一部分分。
考虑 的部分分,不难发现,我们可以考虑一个后缀 ,然后考虑前缀的最大数码 ,我们预处理出来这种情况把后缀减到 需要的步数和减完之后的负数部分即可。
发现上述做法的负数部分绝对值是小于 的,这意味着剩下的部分全部都是 ,考虑设 表示个位为 ,前面紧跟着 个 ,除此以外的前缀的最大数码为 ,将该后缀减到小于 需要的最少步数,同时设 表示操作完之后的个位是什么。
预处理,然后xjb转移即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
ll pw[20],ans,x;
int md[107];
ll dp[20][10][10];
int nx[20][10][10];
inline int mxd(ll x){
int d=0;
while(x){
d=std::max(d,md[x%pw[2]]);
x/=pw[2];
}return d;
}
inline int get(int i){
return x/pw[i]%10;
}
void Main(){
for(int re i=pw[0]=1;i<=18;++i)
pw[i]=pw[i-1]*10;
for(int re i=1;i<=1e2;++i)
md[i]=std::max(md[i/10],i%10);
for(int re j=0;j<10;++j)
for(int re k=0;k<10;++k)
if(j<k){
dp[0][j][k]=1;
nx[0][j][k]=10+j-k;
}else {
dp[0][j][k]=2;
nx[0][j][k]=10-k;
}
for(int re i=1;i<=18;++i)
for(int re j=0;j<10;++j)
for(int k=0;k<10;++k){
nx[i][j][k]=j;
for(int nw=9;nw>=0;--nw){
dp[i][j][k]+=dp[i-1][nx[i][j][k]][std::max(k,nw)];
nx[i][j][k]=nx[i-1][nx[i][j][k]][std::max(k,nw)];
}
}
std::cin>>x;
for(int re i=1;i<=18&&pw[i]<=x;++i)
while(get(i)!=9){
int d=mxd(x/pw[i]);if(!d)break;
int t=x%10;
ans+=dp[i-1][t][d];
x-=t-nx[i-1][t][d];
x-=pw[i];
}
for(int re i=18;i;--i)
while(get(i)){
int d=get(i),t=x%10;
ans+=dp[i-1][t][d];
x-=t-nx[i-1][t][d];
x-=pw[i];
}
if(x)++ans;cout<<ans<<"\n";
}
inline void file(){
#ifdef zxyoi
freopen("number.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}