算法题:奇怪的数

题目描述

要求n位数的数字A中任意5位的和不能超过m,而且奇数位必须时奇数,偶数位必须是偶数,问这样的数字A有多少种,要求输出结果与998244353求模后的数字。输入条件: 5 ≤ n ≤ 2 × 1 0 5 5 \le n \le2\times10^5 5n2×105 0 ≤ m ≤ 50 0 \le m \le 50 0m50

解析

此题应该使用动态规划。设 p i p_i pi表示数 i i i在A中的位次, f ( p i ) f(p_i) f(pi)表示A中前 p i p_i pi位满足题设条件的分布种数。设dp[a][b][c][d]表示后4位数分别选择a、b、c、d时前 p a p_a pa位数字的可能分布情况。则有 d p ′ [ a ] [ b ] [ c ] [ d ] dp'[a][b][c][d] dp[a][b][c][d] = ∑ e : 奇 / 偶 , a + b + c + d + e < = m d p [ b ] [ c ] [ d ] [ e ] 。 \sum_{e : 奇/偶, a + b + c + d + e <= m}dp[b][c][d][e]。 e:/,a+b+c+d+e<=mdp[b][c][d][e]如下图所示, p a = 6 p_a=6 pa=6, f ( p a ) = ∑ a , b , c , d d p [ a ] [ b ] [ c ] [ d ] f(p_a) = \sum_{a, b, c, d}dp[a][b][c][d] f(pa)=a,b,c,ddp[a][b][c][d]。需要注意,在遍历a,b,c,d,e时都要考虑奇偶。
在这里插入图片描述
这个状态转移公式怎么来的呢,注意看上图中1-5这五位与2-6这五位有4位是共用的,当2-6位中每有一个符合“和不超过m”的分布时,需要加上该分布下第2位之前的数的可能分布数量,才能得到该分布下前6位数字可能的分布数量(即dp[a][b][c][d])。那么该分布下第2位之前的符合条件的分布数量就是dp[b][c][d][e],2-6位中只有这前4位e、d、c、b与前面有关。

代码

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

#define MOD  998244353      // 把MOD声明为预编译符号或者静态常量可以很大提高性能
int dp[2][10][10][10][10];
int m, n;

int main()
{
    // 请在此输入您的代码
    cin >> n >> m;

    // 初始化
    for (int a = 0; a < 10; a += 2)
        for (int b = 1; b < 10; b += 2)
            for (int c = 0; c < 10; c += 2)
                for (int d = 1; d < 10; d += 2)
                    dp[1][a][b][c][d] = 1;
    
    // 迭代
    for (int i = 5; i <= n; ++i)
    {
        int p = i % 2;
        for (int a = p; a < 10; a += 2)
            for(int b = 1 - p; b < 10; b += 2)
                for (int c = p; c < 10; c += 2)
                    for (int d = 1 - p; d < 10; d += 2)
                    {
                        int res = 0;
                        auto &temp = dp[i % 2][b][c][d];
                        for (int k = p; k < 10; k += 2)
                            if (a + b + c + d + k <= m)
                                res = (res  + temp[k]) % MOD;
                        dp[1 - p][a][b][c][d] = res;
                    }
    }

    // 得出结果
    int ans = 0;
    int p = n % 2;
    for (int a = p; a < 10; a += 2)
        for(int b = 1 - p; b < 10; b += 2)
            for (int c = p; c < 10; c += 2)
                for (int d = 1 - p; d < 10; d += 2)
                {
                    ans = (ans + dp[1 - p][a][b][c][d]) % MOD;
                }
    cout << ans << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HilariousDog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值