数位DP

Posted emcikem

tags:

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

数位DP由来

先来一道题

传送门
给出一个闭区间,求闭区间里的round number数(只要该数字二进制中0的个数≥1的个数,就是round number)
(2^{31})就可以等于2e9了,所以很容易可以得到以下代码

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
int check(ll x){
    ll a=0,b=0;
    while(x){
        if(x%2==0)a++;
        else b++;
        x/=2;
    }
    return a>=b?1:0;
}
int main(){
    ll s,f;
    while(cin>>s>>f){
        ll ans=0;
        for(ll i=s;i<=f;i++){
            if(check(i)){
                ans++;
            }
        }
        printf("%lld
",ans);
    }
    return 0;
}

然而,光荣地til了
技术图片

其实呢,可以看到计算一个数字的时间复杂度是O(31)也就是O(1)
但是计算的是一个区间,复杂度达到了O(2e9)

区位DP

数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp咯。数位还算是比较好听的名字,数位的含义:一个数有个位、十位、百位、千位......数的每一位就是数位啦!
所以dp开的大小就可以是几位数,dp[i] 就表示小于等于 (10^{i}) 的数中
之所以要引入数位的概念完全就是为了dp。数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了。

一般应用于:

  • 求出在给定区间 [A,B] 内,符合条件 P(i) 的数 i 的个数.
  • 条件 P(i) 一般与 数 的大小无关,而与 数的组成 有关.




先引入一个简单的题
传送门
求[1,1e10]中数字没有62或4的数的个数

定义dp[i][j]表示位数为i,最高位为j的时,符合题意的个数
dp[1][2]=1表示一位,且最高位为2,就是2,符合的情况有1个,就是2
dp[2][0]=9表示二位,且最高位是0,就是0-9,符合情况的有9个
dp[2][6]=8表示二位,且最高位是6,就是60-69,符合情况的有8个

比如位数为2时,dp[2][j]+=dp[1][k],把0-9的情况都加上去
比如位数为3时,dp[3][j]+=dp[2][k],把10-99的情况都加上
比如位数为4时,dp[4][j]+=dp[3][k],把100-999的情况都加上

if(j==4){//最高位为4
    dp[i][j]=0;
}else if(j==6){//最高位为6
    for(int k=0;k<=9;k++){
        if(k!=2)dp[i][j]+=dp[i-1][k];//如果个位为2,不变,如果不为2,最高位变成之前的一位,最高位进行枚举
    }
}else{//其他情况
    for(int k=0;k<=9;k++){
        do[i][j]+=dp[i-1][k];
    }
}



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

数位dp小练

动态规划_计数类dp_数位统计dp_状态压缩dp_树形dp_记忆化搜索

数位DP

数位dp

HDU 2089 数位dp入门

数位DP