幸运数字 题解

题目

题目

要解决这个问题,我们需要找到同时满足以下两个条件的数字:

  1. 数字的数码和等于 y
  2. 数字对 z 取余等于 0

由于数字可能非常大,我们不能直接枚举所有可能的数字,而是需要使用动态规划(Dynamic Programming, DP)的方法来解决这个问题。

解题思路:

  1. 状态定义

    • 我们定义一个二维数组 dp[i][j],其中 i 表示当前数码和,j 表示当前数字对 z 取余的结果。
    • dp[i][j] 表示数码和为 i,且对 z 取余为 j 的数字的个数。
  2. 状态转移

    • 对于每一个数码和 i,我们从 19 枚举下一个数字 d(因为数字不能包含 0)。
    • 更新新的数码和 i + d 和新的余数 (j * 10 + d) % z
    • dp[i + d][(j * 10 + d) % z] 增加 dp[i][j]
  3. 初始条件

    • dp[0][0] = 1,表示数码和为 0 且余数为 0 的情况有一种(即空数字)。
  4. 结果

    • 最终的结果是 dp[y][0],即数码和为 y 且对 z 取余为 0 的数字的个数。
  5. 取模

    • 由于结果可能非常大,我们需要对结果取模 1000000007

代码实现:

#include <iostream>
#include <vector>
using namespace std;

const int MOD = 1000000007;

int countLuckyNumbers(int y, int z) {
    if (。。。) 。。。 // z不能为0

    // dp[i][j] 表示数码和为i,且对z取余为j的数字的个数
    vector<vector<int>> dp(y + 1, vector<int>(z, 0));
    dp[0][0] = 1;

    for (int i = 0; i <= y; ++i) {
        for (int j = 0; j < z; ++j) {
            如果没有可能的数字,跳过

            for () { // 枚举下一个数字
               如果数码和超过y,跳过

                int new_j = (j * 10 + d) % z;
                dp[i + d][new_j] = (dp[i + d][new_j] + dp[i][j]) % MOD;
            }
        }
    }

    return dp[y][0];
}

int main() {
    int y, z;
    cin >> y >> z;
    cout << countLuckyNumbers(y, z) << endl;
    return 0;
}

代码解释:

  • 初始化:我们初始化一个二维数组 dp,大小为 (y+1) x z,并将其所有元素初始化为 0dp[0][0] 初始化为 1,表示空数字的情况。
  • 状态转移:我们遍历所有可能的数码和 i 和余数 j,然后枚举下一个数字 d(从 19),并更新新的数码和和余数。
  • 结果:最终的结果是 dp[y][0],即数码和为 y 且对 z 取余为 0 的数字的个数。

复杂度分析:

  • 时间复杂度O(y * z * 9),其中 y 是数码和的上限,z 是取余的模数。
  • 空间复杂度O(y * z),用于存储动态规划表。

这个算法在给定的约束条件下是高效的,能够在合理的时间内计算出结果。

1 个赞