数位DP
问题
求区间中满足条件的数有多少个, ,该条件与数位有关,比如不包含数字。
思路
考虑函数表示区间中满足条件的数的个数,那么区间中满足条件的数的个数为。
然后用动态规划的方法求。求出的每一位数字以及长度,用数组表示长度为首位为的满足条件的数的个数,那么表示长度为的满足条件的后缀的个数,而表示所有长度小于的满足条件的数的个数,令。
最后求出所有长度为的满足条件的数的个数。对于从高位到低位,计算所有最高的位与相同且第位比小的满足条件的数的个数,设的第位数字为,则,如果从第位开始不满足条件就退出循环。若本身满足条件,则。那么。
时间复杂度
。实际上为的位数,而的位数可以表示为,即,默认为以为底的对数。
测试
模板
#include <iostream>
using namespace std;
#include <cstdio>
#include <cstring>
typedef long long LL;
const int N = 20; // the maximal number of digits
LL dp[N][10]; // dp[i][j] is the amount of numbers with i digits and first digit j
int nums[N]; // the digits of n
/**
* @param n: the maximal number
* @return: the amount of numbers from 0 to n
*/
LL cal(LL n) {
int len = 0; // the length of n
// nums[i] is the i-th digit of n from right to left
while (n > 0) {
nums[++len] = n % 10;
n /= 10;
}
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= len; ++i) {
for (int j = 0; j <= 9; ++j) {
/*
update: dp[i][j]
*/
}
}
LL ans = dp[len][0]; // the numbers with length shorter than n
dp[len][0] = 0;
int pos; // the position of the digits of n
for (pos = len; pos >= 1; --pos) {
for (int j = 0; j < nums[pos]; ++j) {
/*
condition: continue
*/
ans += dp[pos][j]; // the numbers less than n
}
/*
condition: break
*/
if (dp[pos][nums[pos]] == 0) break; // no more numbers
}
if (pos == 0) ++ans; // n
return ans;
}