【Atcoder dp_s】Digit Sum(数位DP)

本文是关于数位DP的复习,介绍了如何定义状态,并以无限制条件为例,探讨满足特定条件的数位组合方案数。重点在于理解如何处理前x个数的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数位DP裸题
好久没写数位DP了,复习一下数位DP的写法
记住怎样定义状态,前x个数,无limit,满足条件的方案数。
一定是前x个数!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int MAXN=10005;
const LL MOD=1000000007;


int num[MAXN];
char s[MAXN];
LL f[MAXN][
### 数位动态规划(Digit DP)概述 数位动态规划是一种专门针对数字问题设计的动态规划技术,主要用于处理涉及数字各个数位特性的计数类问题。通过逐位分析数字并记录状态转移关系,能够高效求解满足特定条件的数字数量。 #### 基本概念 数位DP的核心在于将一个大范围内的数字分解成其每一位上的数值,并基于这些数值构建动态规划的状态空间。这种方法特别适合于解决那些需要枚举大量数字但又可以通过某些约束减少搜索空间的问题[^2]。 #### 动态规划状态定义 在典型的数位DP实现中,常用的状态变量包括当前正在考虑的数位位置以及一些辅助信息来描述已经填充的部分所具有的性质。例如,在LeetCode题目“统计整数数目”中提到的一种常见做法是使用二维数组`dp[i][j]`表示当数字被写到第 `i` 位时,前缀和等于 `j` 的合法方案总数[^3]。 然而更通用的形式可能还需要额外维度用来存储其他限制条件比如是否有前置零或者是否严格小于给定上限等标志位。因此完整的状态可能会扩展为三元组形式 `(pos, sum, tight)` ,其中: - `pos`: 当前处理的是哪一位; - `sum`: 已经累积起来的某项属性总值 (如各位数字加权后的结果); - `tight`: 表明目前形成的序列是否紧贴着输入边界(即前面所有位都取最大允许值),这有助于后续判断剩余可选范围。 #### 转移方程 对于每一个新的待决定的位置 pos 和之前累计的结果 sum 及布尔型变量 tight 来说,下一步应该尝试所有的可能性 d (d 属于 {0,...,9} 或者受限于 upper_limit 如果存在的话),更新下一个状态: ```python for d in range(start_digit, end_digit + 1): new_sum = calculate_new_sum(sum, d) # 更新累加器逻辑取决于具体问题需求 next_tight = True if (tight and d == max_digit_allowed_at_pos[pos]) else False dp[new_pos][new_sum][next_tight] += dp[pos][sum][tight] ``` 这里需要注意两点:一是如何初始化 base case;二是最终答案往往是从多个初始起点出发经过一系列有效转换之后得到的整体贡献集合。 #### 实际案例解析 以 LeetCode 题目为例,“统计整数数目”的目标是在不超过某个固定界限的前提下找出具有指定特征的所有正整数的数量。按照上述框架搭建解决方案如下所示: ```python def countNumbersWithSpecificProperty(n): s = str(n) length = len(s) @lru_cache(None) def dfs(pos, total_sum, is_limit, has_non_zero): if pos == length: return int(has_non_zero) * bool(total_sum % some_condition == target_value) res = 0 up = int(s[pos]) if is_limit else 9 for digit in range(up + 1): new_total_sum = total_sum + digit if has_non_zero or digit != 0 else 0 res += dfs( pos + 1, new_total_sum, is_limit and digit == up, has_non_zero or digit != 0 ) return res return dfs(0, 0, True, False) ``` 此函数采用记忆化递归来代替显式的表格结构维护中间过程数据,从而简化代码书写难度的同时保持较高的执行性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值