数位DP学习笔记
- 求给定区间 [ l , r ] [l, r] [l,r]符合条件 f ( i ) f(i) f(i)的数的个数
- f ( i ) f(i) f(i)一般与数的大小无关,而与数的组成有关
数位DP
记忆化搜索
- 从起点向下搜索,到最底层得到方案数,一层一层向上返回答案并累加,最后从搜索起点得到最终答案
- 对于 [ l , r ] [l, r] [l,r]区间问题,我们一般把他转化为两次数位DP,即找` [ 0 , r ] [0, r] [0,r]和 [ 0 , l − 1 ] [0, l - 1] [0,l−1] 两段,再将结果相减就得到了我们需要的 [ l , r ] [l, r] [l,r]
状态
- 数位DP基本的量数字位数 p o s pos pos
- 数位的最高位限制 l i m i t limit limit
- 判断前导 0 0 0标记 l e a d lead lead
- 解决数组组成问题时往往需要记录,前一位或前几位, p r e pre pre
- 更多参量,视题意而定
前导 0 0 0标记
在许多情况下,前导 0 0 0会影响判断
我们增加一个前导 0 0 0标记
- 当前位 l e a d = t r u e lead=true lead=true并且当前位为 0 0 0,当前位为前导 0 0 0, p o s + 1 pos+1 pos+1继续搜索
- 当前位 l e a d = t r u e lead=true lead=true但是当前为不为 0 0 0,当前位为本数最高位, p o s + 1 pos+1 pos+1继续搜索
最高位标记 l i m i t limit limit
- 若当前位 l i m i t = t r u e limit=true limit=true而且已经取到了能取到的最高位时,下一位 l i m i t = t r u e limit=true limit=true
- 若当前位 l i m i t = t r u e limit=true limit=true但是没有取到能取到的最高位时,下一位 l i m i t = f a l s e limit=false limit=false
- 若当前位 l i m i t = f a l s e limit=false limit=false时,下一位 l i m i t = f a l s e limit=false limit=false
设这一位标记为 l i m i t limit limit,当前可取的最高为 t o p top top,则下一位的标记为 l i m i t limit limit& & i = = t o p i= = top i==top
DP值的记录和使用
- d p dp dp是在记忆化搜索的框架下进行的,每当找到一种情况我们就可以这种情况记录下来
搜到后面遇到相同的情况时直接使用当前记录的值。
- d p dp dp数组的下标表示的是一种状态,只要当前的状态和之前搜过的某个状态完全一样
我们就可以直接返回原来已经记录下来的 d p dp dp值
代码
LL dfs(int pos, int pre, bool lead, bool limit)//记搜,pos当前位,pre前一位,前导0,最高位
{
if (pos > len) return st; //剪枝,可更改
if ((dp[pos][pre] != -1 && (!limit) && (!lead))) return dp[pos][pre];//记录当前值
LL res = 0; //暂时记录当前方案数
int top = limit ? a[len - pos + 1] : 9;//top当前位能取到的最大值
for (int i = 0; i <= top; i++)
{
//有前导0并且当前位也是前导0
if ((!i) && lead) res += dfs(/**/, /**/, true, i == res && limit);
//有前导0但当前位不是前导0,当前位就是最高位
else if (i && lead) res += dfs(/**/, /**/, false, i == res && limit);
else if (根据题意而定的判断) ret += dfs(/**/, /**/, false, i == res && limit);
}
if (!limit && !lead) dp[pos][pre] = res;//当前状态方案数记录
return res;
}
LL part(LL x)//把数按位拆分
{
len = 0;
while (x) a[++len] = x % 10, x /= 10;
memset(dp, -1, sizeof(dp));//初始化-1(因为有可能某些情况下的方案数是0)
return dfs(1, -1, true, true);//进入记搜
}