Fourth day
数位DP
- 定义:给定区间
l 到r ,在l 到r 内共有多少个满足条件的数
可以先做一个前缀和,则
所以可以转化为
-
\color{orange}0\ \text{到}\ x\ \text{之间有多少个满足条件的数}
求
将
找一个
也就是找到
DP方式就是
-
\color{pink}\text{以上是数位DP统一的设计方式}
- 初始化
f[k+1][1] = 1 填到了k + 1 位 - 转移
-
f[i][0] += f[i-1][0] -
for(int j = 0; j <= x[i-1]; j++) { f[i - 1][x[i - 1] == j] += f[i][j]; }当前
x 与y 的大小关系取决于在y_{i-1} 位上填的数j ,如果j 比x_{i-1} 小,那么关系就从 等于 变成 小于,如果j 等于x_{i-1} 那么依然是相等关系所以第二维状态的转移为x[i - 1] == j
-
- 最终的答案就是 f[1][0] + f[1][1]
\color{pink}\text{数位DP的套路}
- 前缀和转化:
由求[l,r] 转化为求[0,r] - [0,l] - 状态设计:
f[i][j] $j$ 表示填完当前的位后 $\begin{cases}\text{0代表} <x \text{ 决定下一位可以填 0-9}\\ \text{1代表} =x \text{ 决定下一位可以填 0-}x_{i-1}\\\end{cases} - 初始化:
x$ 有 $k$ 位,$f[k+1][1] = 1 - 状态转移:
枚举i - 1 位填什么即可
- 求数的每一位
int k = 0; while(x != 0) { y[++k] = x % 10; x = x / 10; }
例题
-
求在
[L,R] 中的数的数位之和
(注:数位之和表示数的每一位加和,例如123的数位置和为1+2+3=6) -
需要求前缀和,即
[L,R] = [0,R] - [0,L] -
状态:
f[i][j] 和g[i][j] $g[i][j]$ 表示当前已经填的数的各个数位之和 -
转移:
for(int i = k + 1; i >= 2; i--){ for(int j = 0; j <= l; j++){ for(int r = 0; r <= 9; r++){ if(j == 1 && r > y[i - 1]) continue; f[i - 1][j != 0 && (r == y[i - 1])] += f[i][j]; g[i - 1][j != 0 && (r == y[i - 1])] += g[i][j] + f[i][j] * r; } } }答案就是
g[1][0] + g[1][1]
- 求在
[L,R] 中的满足相邻两个数字之差至少为2的数有多少个
在上一问的基础上增加一维,
即,
同时,除此之外还需要
- 对于一个
n 位数\overline{x_1x_2x_3\cdots x_n} ,
函数f(x) = |x_1 - x_2| + |x_2 - x_3| + \cdots + |x_{n - 1} - x_n| - 求,在
[L,R] 之间一个找一个x 使得f(x) 的值最大
求
由此,状态应该修改
增加一维度
另外还需要一个维度
所以状态:
依次枚举上一位的
排列DP
-
定义:有一个
1 到n 的排列, 求满足某个条件的排列有多少种 -
求
1 到n 的排列中,逆序对数量是偶数个的排列的个数
定义状态:
初始化:
枚举方法: 把第
优化方法:第二维度
例题
- 对于一个排列,定义其“激动值”为序列中严格大于前面所有数的元素的个数。
比如, {1,1,5,6,5 } 的激动值为3 。给定n 个数p_1,p_2, \cdots,p_n ,求这n 个数的所有排列中,激动值不超过k 的个数。1≤k≤n≤200,\ 1≤p_i≤200
正着插入第
而倒着插入并不会影响到后面的数,所以选择倒着插入
所以定义状态:
由于插入的
- 有一个
1 到n 的排列 - 求最长上升子序列长度不超过
2 的排列有多少个