E 简洁的数
- 题意:给定区间
,求区间内不超过
个数位在
间的数有多少个 - 区间
,将
区间转换为
求解,但是因为这里
,所以这样转换不太方便。注意到判断单个数是否合法比较容易,就可以转换为
,并且单独判定
是否合法。 - 对于判定区间
中有多少合法的数,我们去从高到低去枚举数位,并判定合法情况计数 - 将
按数位从低到高分解,并且存入
中,其中
代表第
位的数位,
代表第
位时,有
个非
的数的数字的个数 - 从高到低去枚举数位,设当前枚举的位为
,为了确保枚举的数小于
,我们需要知道枚举的
位上的数字是否等于
,若不等于,则
位的数可以取
,否则取
- 对于没有任何限制的数,我们将其记忆化使用。反之直接枚举
#include <bits/stdc++.h>
using namespace std;
#define int unsigned long long
int dp[30][5], num[30];//记忆化,存储数位
//num[i]代表数字的第i位
int dfs(int now, int lim, int cnt) {//从高到低位枚举
if(cnt > 3) return 0;//合法性判断
if(!now) return 1;//此数合法
if(!lim && dp[now][cnt]) return dp[now][cnt];
int Ceil = lim ? num[now] : 9;//确定上界
int ans = 0;
for(int i = 0; i <= Ceil; ++i) {
ans += dfs(now - 1, lim && (i == num[now]), cnt + (i != 0));
}if(!lim) dp[now][cnt] = ans;//记忆化
return ans;
}
int solve(string x) {
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= x.size(); ++i) num[i] = x[x.size() - i] - '0';
return dfs(x.size(), 1, 0);
}
signed main() {
string a, b;
while(cin >> a >> b) {
int flag = 1, cnt = 0;
for(int i = 0; i < a.size(); ++i) {
if(a[i] != '0') ++cnt;
if(cnt > 3) flag = 0;
}//单独判定a是否可行
cout << solve(b) - solve(a) + flag << endl;
}
return 0;
}