【校內模擬】數值修改(貪心)(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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章