视频讲解
一般数位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)