【算法学习01】数位DP

数位DP模版

详细见灵神视频和文字版题解

数位 DP 通用模板_哔哩哔哩_bilibili

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

代码模版(python)

#数位dp
#模版一般四个参数:(i,mask,isLimit,isPreNum)
#i表示填到i位,mask表示之前已经存储的条件的集合,可以不止一个参数,isLimit表示是否超出上限,isPreNum表示是否有前导数,这位有时可以忽略
def find(h:int)->int:
    @cache#记忆化搜索
    def f(i:int,mask:int,isLimit:bool,isPreNum:bool)->int:
        s = str(h)
        n = len(s)
        if i == n:#递归边界
            return int(isPreNum) #and check(mask)判断mask是否合法
        res = 0
        if not isPreNum:#isPreNum ==False 表示存在前导0,说明这一位不取的话也算前导0
            res = f(i+1,mask,False,False)
        up = int(s[i]) if isLimit else 9 #判断是否受上一个数位限制
        low = 1-int(isPreNum) #判断是否存在前导0
        for d in range(low,up+1):#循环遍历目前i的数码
            #mask = .....改变mask的状态
            res+=f(i+1,mask,isLimit and d==up,True)
        return res
    return f(0,0,True,False)#一般第一个数位都受限,有isLimit =True
return find(high)-find(low-1)#一般是判断范围[low,high]有多少个合法数可以这么写

相关题目练习

【leetcode】2827.范围中美丽整数的数目

        题目描述

        数据集范围

        思路

        判断出来这题是用数位dp之后,套模版和考虑递归边界的条件即可。

        题目有两个限制条件:1、偶数数位数目与奇数数位数目相同;2、这个整数能被k整除。

        针对第一个限制条件,我们只需使用mask记录偶数数位数目和奇数数位数目的差,最后在递归边界判断mask是否为0即可。

        而对于第二个限制条件,记录每一个数字的能否被k整除显然是不科学的,因为这意味着有[low,high]个状态,即10^9,因此我们用状态机的思想考虑能否进行状态转移而减少状态数目。考虑到对于任一两位数,例如38,有38\mod k\equiv \left ( \left ( 3 \mod k \right ) *10+8\right)\mod k,那么38对于k的余数可以通过38两个数位对于k的余数得到,那么进行数位dp的时候,第i+1位的余数可以通过第i位的余数和第i+1位的数位对于k的余数获得。因此我们只需要记录k个状态,其中k\le 20

        代码实现

class Solution:
    def numberOfBeautifulIntegers(self, low: int, high: int, k: int) -> int:
        #数位dp
        #模版一般四个参数:(i,mask,isLimit,isPreNum)
        #i表示填到i位,mask表示递归条件,isLimit表示是否超出上限,isPreNum表示是否有前导数
        def find(h:int)->int:
            @cache#记忆化搜索
            def f(i:int,mask:int,isLimit:bool,isPreNum:bool,sub:int)->int:
                s = str(h)
                n = len(s)
                if i == n:
                    return int(isPreNum) and mask == 0 and sub ==0
                res = 0
                if not isPreNum:
                    res = f(i+1,0,False,False,0)
                up = int(s[i]) if isLimit else 9
                low = 1-int(isPreNum)
                for d in range(low,up+1):
                    tmp = 1 if d%2==1 else -1
                    res+=f(i+1,mask+tmp,isLimit and d==up,True,(sub*10+d)%k)
                return res
            return f(0,0,True,False,0)
        return find(high)-find(low-1)

        复杂度分析

  • 时间复杂度:\mathcal O\left ( n^2kD \right ),其中nhigh的十进制长度即n = \log highk为每个数码的状态数,记忆化搜索的时间复杂度为:状态总数*计算每一个状态的时间。状态总数为n^2k,计算每一个状态的时间为D,因此该算法时间复杂度为\mathcal O\left ( n^2kD \right )
  • 空间复杂度:\mathcal O\left ( n^2k \right ),即为搜索的状态总数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值