数位DP模板

Posted liuchanglc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DP模板相关的知识,希望对你有一定的参考价值。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 //数位DP一般数据范围很大,所以要开long long 
 5 ll f[now][zt][da];
 6 //now=当前枚举到的数位,一般从高位到低位枚举
 7 //zt=枚举到上一位时的状态,即now+1位
 8 //da=你想要到达的状态
 9 //eg: 比如你想要求一个数能否被它所有数位上的数字整除
10 //你就可以定义状态为f[当前的数位][上次枚举到的数][之前所有数位的最小公倍数]
11 //当然,f数组也有可能开到4维,看具体情况
12 ll num[20];
13 //num数组记录你要求解的数的每一位上的数字,一般开20就可以了
14 ll dfs(ll pos,ll state,ll lead,ll limit/*,ll zer 前导0,视情况而定*/){
15 //pos当前的位数,state上一位的状态,lead想要达到的状态,limit判断前一位是否为最高位
16 //为什么要判断最高位呢?
17 /*
18 我们来举一个例子
19 比如说我们要求的数为5456
20 我们把它的千位设为第3位,百位设为第2位,十位设为第1位,个位设为第0位
21 当它的第三位为0、1、2、3、4时,它的第二位可以从0枚举到9
22 但是当它的第三位枚举到5时,第二位的数就只能枚举到4
23 所以我们用一个变量limit记录前一位能不能达到最大值,如果上一位达到了最大值,那么这一位就只能枚举到当前位上的数
24 如果上一位没有达到最大值,那么这一位就可以从0到9随便枚举
25 */
26     if(pos<0){
27         if(/*满足情况*/)return 1;
28         else return 0;29     }    
30     //递归边界,即遍历到了最后一位
31     //这时要结合实际情况判断返回1或0
32     if(!limit && f[pos][state][lead]!=-1) return f[pos][state][lead];
33     //记忆化
34     //如果当前的f值已经求出来过,并且没有数位限制,那我们就可以直接用
35     //如果有数位限制,那我们就不能直接用,因为我们不知道这两次的数位限制是否相等
36     ll up=limit?num[pos]:9;  //枚举上界
37     //如果没有限制,随便遍历
38     //有限制,只能遍历到当前位
39     ll ans=0;               //计数
40     for(int i=0;i<=up;i++){
41     //枚举,然后把不同情况的个数加到ans就可以了
42         ans+=dfs(pos-1,/*状态转移*/nowstate,/*当前的状态,视情况而定*/nowlead,
43         /*想要达到的状态,可能变,也可能不变*/limit && i==up);
44         /*最后一个参数是判断最高位
45         ==的优先级要高于位运算,所以其实就是一个判断当前数字是否为该位上的最大数字的操作
46         如果上一位有限制并且当前枚举到的是该数位上合法的最大的数字,我们就传1,否则传0
47         比如2345
48         当我们的千位上枚举到1,百位上枚举到9时,虽然9是最大的数字。但是第一个条件jud==1并不满足,所以这时我们要传0
49         当我们的千位上枚举到2,百位上枚举到3时,这时两个条件都满足,所以这时我们要传1*/
50     }
51     if(!limit) f[pos][state][lead]=ans;
52     //如果没有限制,更新f值
53     return ans;
54     //返回结果
55 }
56 ll solve(ll x){
57     ll tot=0;
58     memset(num,0,sizeof(num));
59     while(x){
60         num[tot++]=x%10;
61         x/=10;
62     }
63     //将x的每一位都扔到数组里,方便求解
64     return dfs(tot-1/*传最高位*/,0/*当前的状态,一般是0*/,0/*想要达到的状态,一般也是0,不过要是有取模、除法什么的还要特殊考虑*/
65     ,1/*上一位数是否达到了最高位,显然达到了,所以传1*/);
66     //有些情况还要判断是不是有前导0,比如统计数字出现的次数,最长上升子序列等等
67     //因为有些情况前导0会对结果造成影响,有些则不会
68 }
69 int main(){
70     ll a,b;
71     scanf("%lld%lld",&a,&b);
72     //一般都是读入两个数,求区间[a,b]中符合要求的数的个数
73     ll ans=solve(b)-solve(a-1);
74     //一般用差分解决问题
75     printf("%lld
",ans);
76     return 0;
77 }

 

以上是关于数位DP模板的主要内容,如果未能解决你的问题,请参考以下文章

数位dp总结 之 从入门到模板

[模板] 数位dp

数位DP模板

数位dp

数位DP套路模板

数位DP套路模板