1 条题解

  • 0

    题意分析:

    定义一个位数为偶数的数为幸运数当且仅当这个数前一半的部分数字之和等于后一半的数字之和,给出一个n,求出有多少个位数小于n的数是幸运数。

    解题思路:

    我们可以比较容易的想到DP,然后我们会发现DP当中我们需要的只是前一半部分或者后一半部分的和不需要具体指导是多少,所以会有很多重复的情况,而如果暴力搜索,那么必然会TLE,所以我们就可以想到记忆化,f[i][j][k]f[i][j][k]表示枚举到第i位时,前一半部分的和为j,后一半部分的和为k的时候,幸运数字的个数为多少,然后转移的方程就是枚举0~9数字,由ijk(i,j,k)转移到i+1j+numk(i+1,j+num,k)或者i+1jk+num(i+1,j,k+num)。这样就会避免很多重复的计算了。

    实现步骤:

    动态规划表初始化:dp[k][s]dp[k][s]表示k位数字之和为s的组合数。 基础情况:1位数字时,数字和为s09s(0-9)的组合数为1。 递推计算:对于k位数字,和为s的组合数等于所有可能的前k1k-1位数字和为sds-d(d为当前位数字)的组合数之和。 结果计算:前halfhalf位和后halfhalf位数字和相等的组合数为所有可能的s对应的dp[half][s]dp[half][s]的平方和。

    代码实现

    #include <iostream>
    #include <vector>
    #include <cstring>
    
    using namespace std;
    
    const int MAX_DIGITS = 10;
    
    int count_lucky_tickets(int N) {
        int half = N / 2;
        vector<vector<int> > dp(half + 1, vector<int>(half * 9 + 1, 0));
        
        dp[0][0] = 1;
        
        for (int i = 1; i <= half; ++i) {
            for (int j = 0; j <= (i-1)*9; ++j) {
                if (dp[i-1][j] > 0) {
                    for (int d = 0; d <= 9; ++d) {
                        dp[i][j + d] += dp[i-1][j];
                    }
                }
            }
        }
        
        int count = 0;
        for (int s = 0; s <= half * 9; ++s) {
            count += dp[half][s] * dp[half][s];
        }
        
        return count;
    }
    
    int main() {
        int N;
        cin >> N;
        
        if (N % 2 != 0 || N > MAX_DIGITS) {
            cout << 0 << endl;
            return 0;
        }
        
        cout << count_lucky_tickets(N) << endl;
        
        return 0;
    }
    
    • 1