数位DP模板

视频讲解
一般数位DP题目:

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

class Solution:
    def countDigitOne(self, n: int) -> int:
        s = str(n)
        @cache
        def dfs(i: int, cnt1: int, is_limit: bool) -> int:
            if i == len(s):
                return cnt1
            res = 0
            up = int(s[i]) if is_limit else 9
            for d in range(up + 1):  # 枚举要填入的数字 d
                res += dfs(i + 1, cnt1 + (d == 1), is_limit and d == up)
            return res
        return dfs(0, 0, True)

给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。例如,如果 digits = [‘1’,‘3’,‘5’],我们可以写数字,如 ‘13’, ‘551’, 和 ‘1351315’。
返回 可以生成的小于或等于给定整数 n 的正整数的个数

class Solution:
    def atMostNGivenDigitSet(self, digits: List[str], n: int) -> int:
        s = str(n)
        @cache
        def f(i: int, is_limit: bool, is_num: bool) -> int:
            if i == len(s): return int(is_num)  # 如果填了数字,则为 1 种合法方案
            res = 0
            if not is_num:  # 前面不填数字,那么可以跳过当前数位,也不填数字
                # is_limit 改为 False,因为没有填数字,位数都比 n 要短,自然不会受到 n 的约束
                # is_num 仍然为 False,因为没有填任何数字
                res = f(i + 1, False, False)
            up = s[i] if is_limit else '9'  # 根据是否受到约束,决定可以填的数字的上限
            # 注意:对于一般的题目而言,如果此时 is_num 为 False,则必须从 1 开始枚举,由于本题 digits 没有 0,所以无需处理这种情况
            for d in digits:  # 枚举要填入的数字 d
                if d > up: break  # d 超过上限,由于 digits 是有序的,后面的 d 都会超过上限,故退出循环
                # is_limit:如果当前受到 n 的约束,且填的数字等于上限,那么后面仍然会受到 n 的约束
                # is_num 为 True,因为填了数字
                res += f(i + 1, is_limit and d == up, True)
            return res
        return f(0, True, False)

给你一个整数n和x,返回[1,n]中整数num表示的二进制中1x,2x,3x等1的个数

from functools import cache

num = 7
x = 1


@cache
def dfs(i: int, cnt1: int, is_limit: bool) -> int:
    if i == 0:
        return cnt1
    res = 0
    up = num >> (i - 1) & 1 if is_limit else 1
    for d in range(up + 1):  # 枚举要填入的数字 d
        res += dfs(i - 1, cnt1 + (d == 1 and i % x == 0), is_limit and d == up)
    return res


r = dfs(num.bit_length(), 0, True)
print(r)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值