簡要題意:
給一個十進制數,每次可以選擇它的一個數碼,然後它的值減去你選擇的這個數碼。
重複這個操作,直到這個數變成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;}