数位DP模板

一、 数位DP

题目为:求[L, R]范围中有多少个满足条件的数字:先求[1,R]区间满足条件的个数,再求[0,L]区间,然后相减,这样做可以不考虑下界的限制条件。

限制条件:数字的每一位之间具有某种性质

此时可以使用数位DP

二、函数概念

处理上述问题时,一般我们使用枚举的方法,循环测试给定区间内的所有数字,并判断当前数字是否满足约束条件。在判定时通常也要使用循环,因此造成时间复杂度升高。

数位DP使用空间换取时间,使用递归,直接从每一数位开始构造数字,构造的同时判断是否满足条件,相对来说时间复杂度低。

常用参数:

1. pos: 当前执行的数位,从第1位开始而不是第0位

2. pre: pos的上一位的,用于检测限制条件 注:pos是数位,而pre是数值

3. limit: 限制条件,决定pos可取的数字范围

4. 根据题目要求,适当添加额外的参数

DFS函数体模板(python):

def dfs(pos: int, pre: int, limit: bool) -> int:
    if pos == len(#): #:给定的范围R
        return 1  # 当执行到第0位时证明所有可能性都已构造完成,返回1或判断参数(数字最小位是1)

    posmax = upperbound[pos] if limit else 9  # 确定当前数位可取的最大值
    count = 0

    for i in range(posmax + 1):
        # 在此处进行判断,根据题目要求决定
        count += dfs(pos + 1, i, limit and i == posmax)

    return count

加入DP 注:如果只运行一次函数,在某些情况中不会有重复的pos和pre对出现,导致DP可能并没有提升的效果

def dfs(pos: int, pre: int , limit: bool) -> int:

    if pos == len(#):
        return 1

    if not limit and dp[pos][pre] != None: return dp[pos][pre]  # 如果算过相同的值,直接返回
    posmax = upperbound[pos] if limit else 9 # 确定当前数位可取的最大值
    count = 0

    for i in range(posmax + 1):
        # 在此处进行判断,根据题目要求决定
        count += dfs(pos + 1, i, limit and i == posmax)

    if not limit:
        dp[pos][pre] = count # 如果limit == false,则递归中limit参数永远为false,因此可以记录下来反复使用结果

    return count

函数详解:

使用dfs进行遍历,从数字的最高位开始构造:最高位受到限制,limit取True。此时可取的值为[0, posmax],使用递归的方法对下一位进行处理。如果本位的值低于posmax,则下一位不受限制,同时如果本位本就不受限制,则下一位一定不受限制(限制 = 123,num = 12x,则x可以取0123;num = 11x,则x可以取0-9;num = 09x,x一样可以取0-9)。所以在调用递归函数时,下一位的limit参数应该是limit and i == posmax,同时对上一位是否等于极限和本位是否等于极限进行判断。

在递归的最后,当pos = len时也就说明该数字构造完成,返回1,根据特殊题目可能返回一个参数表示该数是否满足条件。count即表示这些return的和。

LeetCode题目:

我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。

如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方(在这种情况下,它们以不同的方向旋转,换句话说,2 和 5 互为镜像);6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。

现在我们有一个正整数 N, 计算从 1 到 N 中有多少个数 X 是好数?

 def rotatedDigits(self, n: int) -> int:
        upperbound = [int(x) for x in str(n)]
        def dfs(pos: int, limit: bool , has: bool) -> int:
            # has表示构造得到的数字是否含有2569
            if pos == len(str(n)):
                return has 
            posmax = upperbound[pos] if limit else 9 
            count = 0
            for i in range(posmax + 1):     
                if i in [0,1,2,5,6,8,9]:
                    count += dfs(pos + 1, limit and i == posmax, has or i in [2,5,6,9])
                    # has参数记载本位以及所有上位中是否存在过2569
            return count
        return dfs(0, True, 0)

本题不需要对上位数值考虑,所以不包含pre参数,但包含has参数,用于记录是否出现过必须存在的值2569,如果存在过,则该数字满足条件,存放在count中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值