不想起标题

蓝桥杯波动数列

题目描述:
观察这个数列:
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 个数,前 id(d表示+a或-b) 的和模 n 的余数为 j 的选法数量。
  • 具体来说,dp[i][j] 表示了一种状态,即选了 i 个数,且它们的和模 n 的余数为 j
  • i:表示当前选了多少个数。在动态规划的过程中,我们会从 i = 0 开始递推,逐渐增加到 n
  • j:表示当前的余数。在求解过程中,我们需要考虑每个数列的和模 n 的余数,因此 j 表示了这个余数。

数组的大小应该足够容纳所有可能的状态。在这个问题中,长度为 n 的数列的和模 n 可以取值范围为 0n-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]

在递推公式中,我们考虑了数列的长度增加,而 ab 是数列每一项增加或减少的值。当数列长度增加时,每一项增加或减少的值也应该相应地增加。因此,在递推公式中,我们将 ab 分别乘以当前数列的长度 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))

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值