【校内模拟】数值修改(贪心)(DP)

简要题意:

给一个十进制数,每次可以选择它的一个数码,然后它的值减去你选择的这个数码。

重复这个操作,直到这个数变成0,问最少需要多少次操作。

x1e18x\leq 1e18


脑子卡住是什么感觉。

就是发现了正解要用的性质,不知道为什么就是没有继续往下细想。

啊艹为什么

题解:

首先容易注意到答案是单调不减的。

要证明这一点可以归纳,考虑 iii+1i+1 假设之前的一直都是单调,容易注意到 i+1i+1 的最大数码不可能比 ii 大超过 11,那么显然 i+1i+1 能够到达的最小的数一定不会比 ii 小,而前面的数全部满足单调性,所以 iii+1i+1 处满足单调性。

于是非常显然我们需要贪心减掉最大的数码。

考虑模拟这个过程,可以拿到一部分分。

考虑 x1e12x\leq 1e12 的部分分,不难发现,我们可以考虑一个后缀 sfsf,然后考虑前缀的最大数码 dd,我们预处理出来这种情况把后缀减到0\leq 0 需要的步数和减完之后的负数部分即可。

发现上述做法的负数部分绝对值是小于 1010 的,这意味着剩下的部分全部都是 99,考虑设 dp[i][j][k]dp[i][j][k] 表示个位为 jj ,前面紧跟着 ii99 ,除此以外的前缀的最大数码为 kk,将该后缀减到小于 00 需要的最少步数,同时设 nx[i][j][k]nx[i][j][k] 表示操作完之后的个位是什么。

预处理,然后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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章