题目:https://www.acwing.com/problem/content/1084/
题意:某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 123,446。现在大家决定玩一个游戏,指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。
1≤a≤b≤2^31 - 1
题解:数位dp。
首先:要求[l,r]内符合题目限制的数的个数,那么只需要求出[0,r]的个数和 [0,l-1]的个数,两个相互一减,就是[l,r]的个数。
其次:从最高位开始分析,假设最高位是x,如果这一位选择填[0,x-1]中的某个数,那么后面那的那些位就可以随便的填(当然要满足题目的限制条件),也就是图中的左子树(这个怎么求,等会再讲,只需要明白,这里可以经过预处理直接O(1)得到答案即可),右子树就是这一位选择x,继续往下走,这样一直走到最后,所有的左子树的方案数再加上最后一个右节点的方案数(本身是不是),就是最后的答案。
左子树应该怎么求:数位dp的分析方法基本都是这样,只不过就是左子树怎么求不一样,不同的题不同的做法,得现场分析,如果时间复杂度允许,甚至暴力直接求也可以,毕竟很多题的位数一般不高,所以树的深度也不高。
这个题求左子树,从已知的条件可以知道,现在能用的信息是,总共还有几位需要填,并且最高位填了某一个数,在这俩个限制的情况下,这个位数总共能够填多少种方案,这里就可以用一个dp来欲出来答案,f[i][j]表示最高位是j,一共有i位的方案数。
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define pii pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
typedef unsigned long long LL;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;
const int N = 15;
int f[N][N]; // f[i,j]表示一共有i位,且最高位填j的数的个数
void init() {
//dp预处理出来,确定最高位后,可以得到的所有方案数
for (int i = 0; i <= 9; i++) f[1][i] = 1;
for (int i = 2; i < N; i++)
for (int j = 0; j <= 9; j++)
for (int k = j; k <= 9; k++) f[i][j] += f[i - 1][k];
}
int dp(int n) {
//如果是0的情况,直接返回值,一位下面nums中没有数,可能会出问题
if (!n) return 1;
vector<int> nums;
//获得某一位
while (n) nums.emplace_back(n % 10), n /= 10;
int res = 0; //记录答案
int last = 0; //记录上一位是多少
//从最高位开始枚举
for (int i = nums.size() - 1; i >= 0; i--) {
int x = nums[i];
//枚举这一位能够选择的比x小的数量,即树的左子树的答案
for (int j = last; j < x; j++) {
res += f[i + 1][j];
}
//如果出现了这一位比上一位下,那么接下来的情况都不符合
if (x < last) break;
//往下一位走
last = x;
//如果走到了最后一位,也就是0位,没有break的话,那么这个数本身也是
//所以也要 ++
if (!i) res++;
}
return res;
}
signed main() {
init();
int l, r;
//求[0,r]的所有符合的数,再减去[0,l-1]所以符合的数,就是这个区间符合的数
while (cin >> l >> r) printf("%d\n", dp(r) - dp(l - 1));
return 0;
}