【暖*墟】 #动态规划# 数位DP方法总结

数位DP

是一种计数用的dp,一般是要统计一个区间[le,ri]内满足一些条件数的个数

一般问题:求出在给定区间 [ A , B ] 内,符合条件 P ( i ) 的数 i 的个数。

条件 P ( i ) 一般与数的大小无关,而与 数的组成 有关。

即:给定一些限制条件,求满足限制条件的第K小的数是多少。

利用数位的性质,设计log级别复杂度的算法。

最基本的思想是”逐位确定“,其预处理的过程也可以看做数位DP。

数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,并且记忆化。

 

【枚举方法1】对于一个求区间[le,ri]满足条件数的个数,最简单的暴力

for(int i=le;i<=ri;i++)
    if(right(i)) ans++;

然而这样枚举不方便记忆化,或者说根本无状态可言。

 

【枚举方法2】控制上界枚举,从最高位开始往下枚举

例如ri=213:从百位开始枚举,百位可能的情况有0,1,2。

然后每一位枚举都不能让枚举的这个数超过上界213

当百位枚举了1,已经比百位上界2小了,后面数位枚举什么都不可能超过上界。

所以问题就在于:当高位枚举刚好达到上界时,那么紧接着的一位枚举就有上界限制了。

具体的这里如果百位枚举了2,那么十位的枚举情况只能是0到1,

如果前两位枚举了21,最后一位只能是0到3。

对于这一点,需要在代码中设定一个 bool 变量 limit 用来判断枚举范围。

最后一个问题:最高位枚举0,百位枚举0,相当于此时我枚举的这个数最多是两位数...

这样枚举是为了无遗漏的枚举,不过可能会带来前导零的问题。

模板里用lead变量表示。不过不是每个题目都会有影响,前导零不一定会影响我们计数。

由于这种新的枚举只控制了上界所以我们的Main函数总是这样:

(这样能很好的表现出数位DP题目满足的【区间可减性】...)

int main(){
    long long le,ri;
    while(~scanf("%lld%lld",&le,&ri))
        printf("%lld\n",solve(ri)-solve(le-1));
    return 0;
}

 

基本的动态模板

【dp思想】:枚举到当前位置pos(数位),状态state(具体数字)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int a[20];
ll dp[20][state]; //不同题目状态不同,一般是:[位数,该位上的具体数字]

ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/){
//注意:不是每个题都要判断前导零

    if(pos==-1) return 1; //递归边界,按位枚举最低位是0,pos==-1说明这个数的数位枚举完了

    /* 返回1表示枚举的这个数是合法的,需要枚举时每一位都满足题目条件,
       也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。
       不过具体题目不同、或者写法不同的话,不一定要返回1。 */
    
    //记忆化(在此前可能不同题目还能有一些剪枝)
    if(!limit && !lead && dp[pos][state]!=-1) 
        return dp[pos][state];

    int up= limit?a[pos]:9 ; //根据limit判断该位的枚举上界up
    //↑↑↑当limit=true时返回a[pos],否则返回9

    ll ans=0; //开始计数
    for(int i=0;i<=up;i++){ //枚举,然后把不同情况的个数加到ans中
        
        if() ...
        else if()...
        ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos])
        
        /* 当前数位、枚举的数为i时,根据题目的约束条件分类讨论。
           计算不同情况下的个数,根据state变量来保证i的合法性。
           有时state要保存前一位pre,然后分类,并且一定要保存枚举的这个数合法。*/

    }
    
    if(!limit && !lead) dp[pos][state]=ans; //计算完,记录状态
    
    /* 这里对应上面的记忆化,在一定条件下时记录,保证一致性。
       如果约束条件不需要考虑lead,这里的lead就完全不用考虑了。*/
    
    return 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值