📌  相关文章
📜  被M整除并在奇数位具有D的范围内的数字计数(1)

📅  最后修改于: 2023-12-03 14:57:20.773000             🧑  作者: Mango

题目介绍

本题旨在帮助程序员练习在指定范围内统计满足一定条件的数字个数。具体要求为:给定两个正整数 M 和 D,统计区间 [1, N] 中所有被 M 整除并在奇数位具有 D 的数字个数。本题涵盖了多种算法,包括暴力、数位DP等,旨在提高程序员对于算法的灵活运用能力。

输入格式

输入只包含一行,三个整数 M、D 和 N(1≤M≤100,0≤D≤9,1≤N≤10^16),数字之间以空格分隔。

输出格式

输出只包含一行,一个整数,表示区间 [1, N] 中所有被 M 整除并在奇数位具有 D 的数字个数。

算法1:暴力枚举

时间复杂度:$O(N\log N)$

该算法很容易被想到,即对区间内的每个数字判断是否满足条件。时间复杂度为 $O(N\log N)$,实际上比较耗时,只能通过此题部分测试数据。

def count_numbers(M, D, N):
    ans = 0
    for i in range(1, N+1):
        if i % M == 0:
            if str(D) in str(i)[::2]:
                ans += 1
    return ans

算法2:数位 DP

时间复杂度:$O(\log N)$

这是一种 OI 界非常常见的算法,也是此题中的高效算法之一。通过数位 DP 可以直接求解满足条件的数字个数。时间复杂度为 $O(\log N)$,比暴力枚举算法要快很多。

def count_numbers(M, D, N):
    dp = [[0] * 205 for _ in range(205)]
    num = list(map(int, list(str(N)))) # 将整数 N 拆开
    len_n = len(num)

    # 求解各位数的方案数
    # 状态转移:dp[i][j] += dp[i-1][(j - k * num[i - 1]) % M] (k ∈ [0, 9])
    for i in range(10):
        if i % M == num[len_n - 1]:
            dp[1][i % M] = 1
    for i in range(2, len_n + 1):
        for j in range(M):
            for k in range(10):
                dp[i][(j + k * (10 ** (i-1))) % M] += dp[i - 1][(j - k * num[i - 2]) % M]

    # 计算被 M 整除且在奇数位上含有数字 D 的数字个数
    ans = 0
    presum = 0
    for i in range(len_n - 1):
        multiple = (N // (10 ** (i + 1))) * (10 ** i)
        presum += multiple * (i % 2 == 0)
        ans += multiple * dp[len_n - i - 1][(M - D * (10 ** (i % 2))) % M]
    for i in range(1, num[len_n - 1]):
        if i % M == D:
            ans += (presum + (10 ** (len_n - 1))) * dp[len_n][((M - i) % M + M) % M]
    if num[len_n - 1] % M == D:
        ans += presum + 1

    return ans

总结

本题中,考察了程序员对算法的灵活运用能力。通过暴力和数位 DP 两种算法,使程序员更好地理解并掌握了动态规划算法,加深了对算法思想的理解。