不降数 题解

题目描述

某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 \(123,446\)

现在大家决定玩一个游戏,指定一个整数闭区间 \([a,b]\),问这个区间内有多少个不降数。

注意:不降数不能包含前导零。

分析

数位 dp。

题目描述可以转换位求 \([1,n]\) 范围内的不降数个数,然后利用 solve(b)-solve(a-1) 得到区间 \([a,b]\) 的结果。

\(dp_{i,j}\) 表示当前处理到第 \(i\) 位,前一位数字是 \(j\) 时的不降数数量。

状态转移即使用记忆化搜索枚举所有数字,注意前导零的情况。当之前位都是前导零时,当前位可以选择 \(0\) 或非零数字。

AC Code

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
ll dp[20][10];
vector<int> m;
ll dfs(int i,int j,bool pd) // pd 记录是否达到上限
{
	if(i==m.size()) return 1;
	if(j!=-1&&!pd&&dp[i][j]!=-1) return dp[i][j];
	int up=pd?m[i]:9;
	ll sum=0;
	for(int k=0;k<=up;k++)
	{
		if(j==-1)
		{
			if(k==0) sum+=dfs(i+1,-1,pd&&(k==up));
			else sum+=dfs(i+1,k,pd&&(k==up));
		}
		else if(k>=j) sum+=dfs(i+1,k,pd&&(k==up));
	}
	if(j!=-1) dp[i][j]=sum;
	return sum;
}
ll solve(ll n)
{
	m.clear();
	while(n)
	{
		m.push_back(n%10);
		n/=10;
	}
	reverse(m.begin(),m.end());
	memset(dp,-1,sizeof(dp));
	return dfs(0,-1,1);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0); 
	ll a,b;
	while(cin>>a>>b) 
	{
		ll ans=solve(b)-solve(a-1);
		cout<<ans<<'\n';
	}
    return 0;
}
posted @ 2025-08-30 18:28  Aron_2023  阅读(10)  评论(0)    收藏  举报