蓝桥杯波动数列
题目描述:
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?输入格式
输入的第一行包含四个整数 n s a b,含义如前面说述。输出格式
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。样例输入
4 10 2 3样例输出
2样例说明
这两个数列分别是2 4 1 3和7 4 1 -2。
首先,我们定义一个动态规划数组 dp
- 我们使用
dp[i][j]
来表示选了i
个数,前i
个d(d表示+a或-b)
的和模n
的余数为j
的选法数量。 - 具体来说,
dp[i][j]
表示了一种状态,即选了i
个数,且它们的和模n
的余数为j
。 i
:表示当前选了多少个数。在动态规划的过程中,我们会从i = 0
开始递推,逐渐增加到n
。j
:表示当前的余数。在求解过程中,我们需要考虑每个数列的和模n
的余数,因此j
表示了这个余数。
数组的大小应该足够容纳所有可能的状态。在这个问题中,长度为 n
的数列的和模 n
可以取值范围为 0
到 n-1
。因此,我们需要一个 (n+1) * (n+1)
的数组来确保所有可能的情况都被覆盖到。
接下来,我们来看递推公式。根据题目的特性,数列中的每一项都是在前一项的基础上加 a
或减 b
得到的。因此,对于长度为 i+1
的数列,我们可以根据前一项的和来推导当前项的和。如果当前项选择加 a
,则和变为 (j - a) % n
;如果当前项选择减 b
,则和变为 (j + b) % n
。因此,递推公式可以写为:
dp[i][j]=dp[i−1][(j−a*i)%n]+dp[i−1][(j+b*i)%n]
在递推公式中,我们考虑了数列的长度增加,而 a
和 b
是数列每一项增加或减少的值。当数列长度增加时,每一项增加或减少的值也应该相应地增加。因此,在递推公式中,我们将 a
和 b
分别乘以当前数列的长度 i
,以确保在每次状态转移中,数列的增量与长度成比例。
最后,我们来初始化 dp
数组。我们需要确保数列的长度至少为 1
,因此初始化时,dp[0][0] = 1
表示长度为 1
的数列的和模 n
等于 0
的方案数为 1
,其余初始化为 0
。
状态方程推导可参考这篇文章
链接:https://blog.csdn.net/m0_63613132/article/details/129640870
下面看看代码:
import os
import sys
def count_sequences(n, s, a, b):
dp = [[0] * (n+1) for _ in range(n+1)]
dp[0][0] = 1
for i in range(1, n+1):
for j in range(n):
dp[i][j] = (dp[i - 1][(j - a*i) % n] + dp[i - 1][(j + b*i) % n]) %(1e8+7)
return int(dp[n - 1][s % n])
n, s, a, b = map(int, input().split())
print(count_sequences(n, s, a, b))