题解:P13890 [蓝桥杯 2023 省 Python A] 奇怪的数
Oberon_Qwl · · 题解
P13890 [蓝桥杯 2023 省 Python A] 奇怪的数 【题解】
一道不错的练手动规
题目简述
要求输出满足以下条件的长度为
-
奇数数位上是奇数
(1,3,5,7,9) -
偶数数位上是偶数
(0,2,4,6,8) -
任意
5 个连续数位的和\le m 显然n 最大2×10^5 ,暴力肯定超,所以依旧动态规划。
思路
核心观察要点
-
每个数位的取值范围固定:奇数位有
5 种选择,偶数位有5 种选择 -
约束条件只涉及连续
5 个数位,因此可以用最近4 个数位的状态来推导新的状态
动规部分
代码实现很简单;
首先定义一个四维 dp 表示最后四个数位的索引为
// v[0]存奇数位,v[1]存偶数位
int v[2][5]={{1,3,5,7,9},{0,2,4,6,8}};
其次,列状态转移方程:当新增第
// 计算5个连续数位的和
int sum=v[aa][a]+v[ab][b]+v[ac][c]+v[ad][d]+v[ae][e];
if (sum<=m)
{
// 状态转移:(a,b,c,d)->(b,c,d,e)
nd[b][c][d][e]=(nd[b][c][d][e]+cnt)%mod;
}
再是初始化:对于长度为
最后滚动数组优化空间
具体迭代过程:
for (int l = 4; l < n; ++l)
{
int nd[5][5][5][5] = {0};//新状态存储数组,作临时存储新状态和状态转移的载体以及滚动更新作用
int res = l + 1; //新增数位的位置
int ae = 1 - (res % 2); //新增数位的取值类型
for (int a = 0; a < 5; ++a) {
for (int b = 0; b < 5; ++b) {
for (int c = 0; c < 5; ++c) {
for (int d = 0; d < 5; ++d) {
int cnt = dp[a][b][c][d];
if (cnt == 0) continue;
int resa = l - 3, aa = 1 - (resa % 2);
int resb = l - 2, ab = 1 - (resb % 2);
int resc = l - 1, ac = 1 - (resc % 2);
int resd = l, ad = 1 - (resd % 2);
for (int e = 0; e < 5; ++e) {
int sum = v[aa][a] + v[ab][b] + v[ac][c] + v[ad][d] + v[ae][e];
if (sum > m) continue;
nd[b][c][d][e] = (nd[b][c][d][e] + cnt) % mod;
}
}
}
}
}
memcpy(dp, nd, sizeof(dp));
}
其他包括四维 dp 的初始化及输出便不展示了。
总结
没文笔不想写了。