// 奇怪的数
// 小蓝最近在找一些奇怪的数,其奇数数位上是奇数,而偶数数位上是偶数同时,这些数的任意5个连续数位的和都不大于m。
// 例如当m=9时,10101和12303 就是奇怪的数,而12345 和11111则不是。
// 小蓝想知道一共有多少个长度为n的上述的奇怪的数。你只需要输出答案对998244353取模的结果。
// 总体解题思想是:
// 任意5个连续数位 --> 意思就是说,我们只要确定 高四位的数字 那么最低位的数字的最大值就是固定的或确定的
// 比如 当m=9时,我们随机确定高四位数 1010 那么最低位的数字只能 <=7 即最大数字为 10107
// 因此以高四位为基础,每次迭代一个数位来DP 从个位开始 每个高四位dp 表示 当高四位为 此四位时,所能得到的符合要求数字的个数的最大值
// 根据以上设置 dp数组 为 dp[2][12][12][12][12] dp[i][a][b][c][d] a表示最低位
// dp[2] 表示 当前四位数的数组 和 上一个四位数的数组 如:十百千万 数位 和 个十百千数位 前四个数位结果将影响后四个数位的结果
// dp后四位表示为 在保持高三位不变的情况下(即dcb) dp[a+2][b][c][d] = dp[a][b][c][d] + (a+b+c+d <= m)
// 即高三位不变的情况下 dp[a][b][c][d] 的结果只对 dp[a+2][b][c][d] 及后面的值有影响
// dp[0] 和 dp[1] 的关系是 (引入 e 表示第五位) 数字(edcba) dp[a][b][c][d] 和 dp[b][c][d][e]
// dp[1][b+2][c][d][e] = dp[1][b][c][d][e] + dp[0][m-b-c-d-e +2][b][c][d] (注意:m-b-c-d-e+2 的值必须与这个数位的奇偶性相同)
// 上面的就是状态转移方程 表示的是 当前dp值 = 前一个数的dp值 + 当前数符合要求(不大于m)的最大数的dp值(关系到a的值)
//
// 找结果时,我们只要固定高三位,遍历累加符合要求的第四位的dp值
#include <iostream>
#include <vector>
using namespace std;
const long MOD = 998244353, M=12;
int n,m;
int dp[2][M][M][M][M];
int p=1,q=0; // 指示i的值 p表示奇数位的初始值,q表示偶数位的初始值
// 初始化dp
void iniDp()
{
// 此时 i=4
for (int a=p; a<10; a+=2)
for (int b=q; b<10; b+=2)
for (int c=p; c<10; c+=2)
for (int d=q; d<10; d+=2){
dp[0][a+2][b][c][d] = dp[0][a][b][c][d] + (a+b+c+d <= m);
}
}
// 计算dp
void slove()
{
// 从第五位数开始
for (int i=5; i<=n; ++i){
p^=1; // 移动一位数 奇偶性发生变化 将值取反
q^=1;
for (int a=p; a<10; a+=2)
for (int b=q; b<10; b+=2)
for (int c=p; c<10; c+=2)
for (int d=q; d<10; d+=2){
// 计算第5个值的范围
int f = m - a - b - c - d;
// q与前一位数的奇偶性是一样的
if ((f&1) != q) --f; // 这里要控制 f与前一个数 是相同 奇偶性
// 当f的值 大于最大值
if (f > q+8) f = q+8;
dp[ i&1 ][a+2][b][c][d] = (dp[ i&1 ][a][b][c][d] + (f < q ? 0 : dp[~i & 1][f+2][a][b][c])) % MOD;
}
}
}
// 求结果
long retAns()
{
long ans=0;
int i = n&1;
for (int b=q; b<10; b+=2)
for (int c=p; c<10; c+=2)
for (int d=q; d<10; d+=2){
// 计算第4和5个值的范围
int f = m - b - c - d;
if (f < p) continue;
// p与当前位数的奇偶性是一样的
if ((f&1) != p) --f; // 这里要控制 f与前一个数 是相同 奇偶性
// 当f的值 大于最大值
if (f > p+8) f = p+8;
ans = (ans + dp[i][f+2][b][c][d]) % MOD;
}
return ans;
}
int main()
{
cin >> n >> m;
iniDp();
slove();
long ans = retAns();
cout << ans << endl;
return 0;
}
【蓝桥杯】奇怪的数
本文介绍了一种使用动态规划方法解决一个问题,寻找满足特定条件的奇怪数,即数的奇数位是奇数,偶数位是偶数,且任何5个连续数位之和不超过m。通过设置dp数组并定义状态转移方程,计算符合条件的奇怪数的数量,并对998244353取模。
2961



被折叠的 条评论
为什么被折叠?



