-
注释:本文的区间都是闭区间
-
讲解视频
00 : 00 − − \small 00:00 --~ 00:00−− 数位 d p \small dp dp原理讲解
02 : 50 − − \small 02:50 --~ 02:50−− “度的数量”思路讲解
21 : 40 − − \small 21:40 --~ 21:40−− “度的数量”代码讲解
36 : 20 − − \small 36:20 --~ 36:20−− “数字游戏”思路讲解
48 : 00 − − \small 48:00 --~ 48:00−− “数字游戏”代码讲解
56 : 50 − − \small 56:50 --~ 56:50−− “Windy 数”思路讲解
65 : 00 − − \small 65:00 --~ 65:00−− “Windy 数”代码讲解
83 : 30 − − \small 83:30 --~ 83:30−− “数字游戏II”思路讲解
94 : 20 − − \small 94:20 --~ 94:20−− “数字游戏II”代码讲解
104 : 10 − − \small 104:10 --~ 104:10−− “不要62”思路讲解
115 : 10 − − \small 115:10 --~ 115:10−− “不要62”代码讲解
127 : 40 − − \small 127:40 --~ 127:40−− “恨 7 不成妻”思路讲解
145 : 10 − − \small 145:10 --~ 145:10−− “恨 7 不成妻”代码讲解 -
数位dp解决的问题:求出给定区间 [ L , R ] \small [L,R] [L,R],符合函数 f ( x ) \small f(x) f(x)的个数。
例:
f ( x ) \small f(x) f(x):不降数—— x \small x x必须满足从左到右各位数字成小于等于的关系。如 123 \small 123 123, 446 \small446 446
L = 1 \small L=1 L=1 , \small , , R = 19 \small R=19 R=19
答案: 18 \small 18 18
解释:只有 10 \small 10 10不是不降数,其余的数都是不降数,故答案为 18 \small 18 18 -
数位dp的技巧:
1 、 1、 1、 d p ( L , R ) = d p ( 0 , R ) − d p ( 0 , L − 1 ) \small dp(L,R)=dp(0,R)-dp(0,L-1) dp(L,R)=dp(0,R)−dp(0,L−1)
对于 [ L , R ] \small [L,R] [L,R]区间问题,我们一般把他转化为两次数位 d p \small dp dp,即找 [ 0 , R ] \small [0,R] [0,R]和 [ 0 , L − 1 ] \small [0,L-1] [0,L−1]两段,再将结果相减就得到了我们需要的 [ L , R ] \small [L,R] [L,R]
例:通过前缀和求区间和
2 、 2、 2、画树 -
模板
#include<vector>
#include<iostream>
using namespace std;
const int N = 100;
int f[N][N];
int u[N];
//根据题目要求来预处理f[i][j]
void init() {
}
//计算[0,n]符合f(x)的个数
int dp(int n) {
//如果L可以取到0的话,特判-1
if (n == -1) return 0;
//特判0
if (n == 0) return 0 / 1;
//用于把每位的数字提取出来
vector<int> vt;
while (n) vt.push_back(n % 10), n /= 10;
//res记录返回值,last保留前缀的信息
int res = 0, last = 0;
for (int i = vt.size() - 1; i >= 0; i--) {
//第一种放法
for (int j = 0; j < vt[i]; j++) {
if (true) res += f[][];
}
//第二种放法就是vt[i]本身,特判vt[i]是否和法,不合法直接退出就好
if (false) break;
}
//返回答案
return res;
}
int main() {
//预处理需要的数据
init();
int l, r;
while (cin >> l >> r) {
//技巧一
cout << dp(r) - dp(l - 1) << endl;
}
return 0;
}