明七暗七(数位dp,二分)

本文深入探讨了一种名为“明七暗七”的算法实现,该算法通过深度搜索策略计算在给定数字中排除所有直接包含7或者可以被7整除的数后的组合数量。文章详细介绍了算法的核心思想,包括动态规划的应用以及递归调用的优化策略。

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

明七暗七

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>

using namespace std;

#define ll long long
#define inf 0x3f3f3f3f
ll dp[20][20];
int bit[20];

ll dfs(int pos, int remainder, int flag) {
    if (pos <= 0) {
        return remainder != 0;
    }
    if (!flag && dp[pos][remainder] != -1) {
        return dp[pos][remainder];
    }
    ll ans = 0;
    int end = flag ? bit[pos] : 9;
    for (int i = 0; i <= end; i++) {
        if (i == 7) {
            continue;
        }
        ans += dfs(pos - 1, (remainder * 10 + i) % 7, flag && i == end);
    }
    if (!flag) {
        dp[pos][remainder] = ans;
    }
    return ans;
}

ll cal(ll n) {
    memset(dp, -1, sizeof(dp));
    int num = 0;
    while (n) {
        bit[++num] = n % 10;
        n /= 10;
    }
    return dfs(num, 0, 1);
}

int main() {
    ll m, n;
    while (scanf("%lld%lld", &m, &n) != EOF) {
        ll temp = m - cal(m);
        ll left = 0, right = (ll) 1e18;
        while (left < right) {
            ll mid = (left + right) / 2;
            if (mid - cal(mid) >= temp + n) {
                right = mid;
//                cout << "right=" << right << endl;
            } else {
                left = mid + 1;
//                cout << "left=" << left << endl;
            }
        }
        printf("%lld\n", left);
    }
}
<think>好的,我现在需要回答用户关于数位DP的问题,包括它的概念、实现方法和应用。首先,我需要回忆数位DP的基本定义和核心思想。根据用户提供的引用[1],数位DP是专注于数字各个数位的动态规划类型,常用于解决与数位相关的问题,比如统计满足特定条件的数字数量。比如统计区间内不包含某个数字的数,或者数字各位之和满足条件的数。 接下来,用户问如何实现数位DP。根据已有的知识,数位DP通常涉及将数字分解成各位,然后按位进行状态转移。实现时需要考虑记忆化搜索,避免重复计算。可能需要预处理数位,处理每一位的可能取值,尤其是前导零和上限的问题。比如,当处理数字123时,第一位如果是0,后面的位可以自由选择,但如果是1,第二位可能受限于2,以此类推。需要设计状态,比如当前位数、是否受限、前导零状态以及其他需要的参数,如各位和等。 关于应用场景,引用[1]提到在蓝桥杯等竞赛中常见,比如统计区间内满足某种数位条件的数字个数,比如windy数、不含4或62的数字等。应用时需要将具体问题转化为状态转移的条件,设计合适的状态和转移方程。 需要确保回答的结构清晰,分点说概念、实现步骤和应用例子。同时,要检查是否引用了提供的参考文献,比如在概念部分引用[1],在动态规划通用方法部分引用[2]。 另外,用户提供的引用[2]提到动态规划的最优子结构和无后效性,这些在数位DP中同样适用。需要指出数位DP满足这些性质,状态设计时要保证无后效性,比如一旦处理到某一位,后续的状态只依赖于当前位的状态,而不关心之前如何到达这里。 现在需要组织这些内容,分概念、实现、应用三个部分。可能还需要示例问题,比如计算[L, R]区间内满足各位数字之和为特定值的数的个数,用来说实现步骤。 需要注意使用中文回答,数学公式用$...$或$$...$$格式,代码块正确使用语法。可能还需要生成相关问题,比如数位DP的经典例题,如何优化数位DP的状态设计,数位DP中的记忆化技巧等。 最后,确保引用的标注正确,比如在提到数位DP概念时添加[^1],在动态规划基本原则时添加[^2]。</think>数位动态规划(数位DP)是一种针对数字各个数位设计的动态规划方法,常用于解决与数字数位相关的计数或条件判断问题。例如统计区间$[L,R]$内满足“各位数字不含4”或“各位和等于特定值”的数字个数。 ### 一、数位DP的核心概念 1. **数位拆分**:将数字按十进制逐位分解,例如$123$分解为$[1,2,3]$。 2. **状态设计**:通常包含当前位数、前导零状态、数位限制(是否达到上限)以及问题相关的参数(如各位和、特定数字标记等)。 3. **记忆化搜索**:通过缓存子问题结果避免重复计算,例如用$dp[pos][sum][limit]$记录处理到第$pos$位、当前和为$sum$、是否受数位限制时的结果[^2]。 ### 二、实现步骤(以统计$[0,N]$中各位和为$T$的数字数量为例) 1. **数位拆分**:将数字$N$转换为字符数组或列表,如`digits = list(str(N))`。 2. **状态定义**:设计动态规划状态,例如: $$dp[pos][sum][is\_limit] = \text{第pos位时和为sum且是否受限制的数字数量}$$ 3. **递归搜索**:按位枚举可能的值,处理前导零和数位限制。 ```python def count_numbers_with_sum(T, N): digits = list(map(int, str(N))) n = len(digits) @lru_cache(maxsize=None) def dfs(pos, current_sum, is_limit): if pos == n: return 1 if current_sum == T else 0 limit = digits[pos] if is_limit else 9 total = 0 for d in range(0, limit + 1): new_limit = is_limit and (d == limit) total += dfs(pos + 1, current_sum + d, new_limit) return total return dfs(0, 0, True) ``` ### 三、典型应用场景 1. **统计区间内不含特定数字的数**:如蓝桥杯“不包含4或62的数字”[^1]。 2. **计算满足数位和条件的数**:如求$[L,R]$中各位和为素数的数的数量。 3. **windy数问题**:相邻数位差的绝对值至少为2的数的计数。 ### 四、关键优化点 1. **状态压缩**:合并冗余状态(如前导零标记`lead`)。 2. **剪枝策略**:提前终止不可能达到目标的分支(如当前和已超过目标值)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值