不降数 题解
题目描述
某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 \(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;
}

浙公网安备 33010602011771号