(待完成)qbxt2019.05 总结2 - 数位DP

数位 DP 严格来说其实并不是 DP……它只是个单纯的计数问题

但是怎么说呢……现在大家似乎都把数位 DP 叫这个名字,所以……我们还是……叫它 DP

额什么是数位 DP 呢?

一句话概括——一类求在 K 进制下m满足条件的数的数量有多少个的算法

常见的问题形式:

求 [l..r] 之间的在 K 进制下满足条件 F 的数有多少个?

什么?你说for (int i = l ; i <= r; i++) ans+=check(i);?

别学竞赛了,回家养鲲吧

我们很快就会发现,数位 DP 的复杂度和位数相关,所以一般数据规模奇大

(以上摘自lyd课件)

数位DP

(以下基于k进制讨论)

数位DP一般会让你求\([l,r]\)满足条件的个数,这里我们设函数\(c(x)\)表示小于x的正整数中满足条件的个数,则最终答案就是 \(c(r+1)-c(l)\) 。但k进制时加一操作不一定好实现,所以更常用的是 \(c(r)-c(l)+\text{对r的特判结果}\)

所以问题转化为求\(c(x)\)

正如它名字中的“数位”,DP状态的第一维通常是数字的位数。所以只要写出转移方程,就可以得到小于\(k^x\)的满足条件的个数(其中x是任意/给定正整数)即\(\sum dp[x][...]\)

现在要求\(c(x)\)。设\(x\)\(l\)位,且现在已经求得了\(dp[l-1][...]\)。现在考虑最高位,分为两种不同的情况:

  1. 满足条件的y的最高位小于x的最高位。这时能保证\(y<x\),所以后面任意选。

  2. 满足条件的y的最高位等于x的最高位。这时可以递归解决后面的位构成的子问题。但我们发现递归完全是浪费,因为子问题求出的dp数组是一样的。所以在子问题中真正有用的是求情况1和递归下一层。

    因为递归的层数是递减的,于是我们可以把递归改为循环。


上面这些抽象的描述说实话我都看不懂QAQ

考虑这样的一道题目:求\([l,r]\)中不包含4和62的数的个数。

那么就可以设\(c(x)\)为小于\(x\)的数中不包含4和62的数的个数,最终答案即为\(c(r+1)-c(l)\)

这里可以设\(dp[i][j]\)表示长度为i位的,最高位是j的满足条件的数的个数。然后我们可以写出这样的转移方程:\(dp[i][j](j\neq4)=sum\{dp[i-1][k]|\text{当}j=6\text{时}k\neq2\}\)

下面假设xl位,最高位是x[1],最低位是x[l],其他类似。这一段代码求出了dp数组。

for(int j=0;j<=9;j++){
    if(j==4)continue;
    dp[1][j]=1;
}
for(int i=2;i<=l;i++){
    for(int j=0;j<=9;j++){
        if(j==4)continue;
        for(int k=0;k<=9;k++){
            if(j==6&&k==2)continue;
            dp[i][j]+=dp[i-1][k];
        }
    }
}

然后考虑情况一。

int ans=0;
for(int j=0;j<x[1];j++){
    if(j==4)continue;
    ans+=dp[l][j];
}

然后是情况二。先写递归版。(toint表示将数组表示的数字转换成int,toarr则相反)

ans+=c(从前面去掉1位的x);

这时我们可以发现,在第一次递归里有用的代码是如下的部分:

for(int j=0;j<x[2];j++){
    if(j==4||(j==2&&x[1]==6)continue;
    ans+=dp[2][j];
}
ans+=c(从前面去掉2位的x);

所以最终我们应该把代码修改为这样子:

int ans=0;
for(int i=l;i>=1;i--){
    for(int j=0;j<x[l-i+1];j++){
        if(j==4||(j==2&&x[l-i]==6))continue;
        ans+=dp[i][j];
    }
}

转载于:https://www.cnblogs.com/water-lift/p/10809884.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值