波动数列

题目链接

分析

设第一个数为 x x x,则第二个数为 x + d 1 x+d_1 x+d1,第三个数为 x + d 1 + d 2 x+d_1+d_2 x+d1+d2 …。这里的 d 1 d_1 d1 d 2 d_2 d2表示 a a a或者 − b -b b,所以这个数列为:

x x x, x + d 1 x + d_1 x+d1, x + d 1 + d 2 x + d_1 + d_2 x+d1+d2, x + d 1 + d 2 + d 3 x + d_1 + d_2 + d_3 x+d1+d2+d3, …, x + d 1 + d 2 + . . . + d n − 1 x + d_1 + d_2 + ... + d_{n-1} x+d1+d2+...+dn1,又因为数列之和为s,所以转化成:

n ∗ x + ( n − 1 ) ∗ d 1 + ( n − 2 ) ∗ d 2 + ( n − 3 ) ∗ d 3 + . . . + d n − 1 = s n * x + (n-1) * d_1 + (n-2) * d_2 + (n-3) * d_3 + ... + d_{n-1} = s nx+(n1)d1+(n2)d2+(n3)d3+...+dn1=s,再在一步转化:

s − [ ( n − 1 ) ∗ d 1 + ( n − 2 ) ∗ d 2 + ( n − 3 ) ∗ d 3 + . . . + d n − 1 ] n = x \frac{s - [(n-1) * d_1 + (n-2) * d_2 + (n-3) * d_3 + ...+ d_{n-1}]}{n} = x ns[(n1)d1+(n2)d2+(n3)d3+...+dn1]=x

因为x是任意整数,所以又转化成:

s s s ( n − 1 ) ∗ d 1 + ( n − 2 ) ∗ d 2 + ( n − 3 ) ∗ d 3 + . . . + d n − 1 (n-1) * d_1 + (n-2) * d_2 + (n-3) * d_3 + ...+ d_{n-1} (n1)d1+(n2)d2+(n3)d3+...+dn1 x x x的余数相同。

到这里就转化成了组合问题。

下面就可以用闫氏dp分析法了。

1.状态表示:f[i][j]表示要选ia或者-b且余数为j的所有集合的数量。
2.状态计算:第i个可以选a或者-b

i个选a ( n − 1 ) ∗ d 1 + ( n − 2 ) ∗ d 2 + ( n − 3 ) ∗ d 3 + . . . + 2 ∗ d n − 2 + a (n-1) * d_1 + (n-2) * d_2 + (n-3) * d_3 +...+ 2 * d_{n-2}+ a (n1)d1+(n2)d2+(n3)d3+...+2dn2+a x = j x = j x=j

则: ( n − 1 ) ∗ d 1 + ( n − 2 ) ∗ d 2 + ( n − 3 ) ∗ d 3 + . . . + 2 ∗ d n − 2 (n-1) * d_1 + (n-2) * d_2 + (n-3) * d_3 +...+ 2 * d_{n-2} (n1)d1+(n2)d2+(n3)d3+...+2dn2 x = j − a x = j - a x=ja

系数和下标之和为n,所以第i项的的系数为n-i

所以:f[i][j] = f[i - 1][j - (n - i) * a]

第i个选b:同理:f[i][j] = f[i - 1][j + (n - i) * b]

时间复杂度 O ( n 2 ) O(n^2) O(n2)

状态数量 * 状态转移时操作数 = (n - 1) * (n - 1) * 2

C++代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010, MOD = 100000007;

int n, s, a, b;
int f[N][N];

int get_mod(int a, int b)
{
    return (a % b + b) % b;
}

int main()
{
    scanf("%d%d%d%d", &n, &s, &a, &b);
    
    f[0][0] = 1;
    for (int i = 1; i < n; i++)
        for (int j = 0; j < n; j++)
            f[i][j] = (f[i - 1][get_mod(j - (n - i) * a, n)] + f[i - 1][get_mod(j + (n  - i) * b, n)]) % MOD;
    
    printf("%d", f[n - 1][get_mod(s, n)]);

    return 0;
}

Java代码

import java.util.Scanner;
public class Main {

    static int N = 1010;
    static int MOD = 100000007;
    static int[][] f = new int[N][N];

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n, s, a, b;
        n = scan.nextInt();
        s = scan.nextInt();
        a = scan.nextInt();
        b = scan.nextInt();
        f[0][0] = 1;
        for (int i = 1; i < n; i++)
            for (int j = 0; j < n; j++)
                f[i][j] = (f[i - 1][getMod(j - (n - i) * a, n)] + f[i - 1][getMod(j + (n - i) * b, n)]) % MOD;
        System.out.println(f[n - 1][getMod(s, n)]);
    }

    public static int getMod(int a, int b) {
        return (a % b + b) % b;
    }
}

参考

ACWing蓝桥杯

### 蓝桥杯 波动数列 算法题 解法 #### 题目概述 波动数列是一道经典的动态规划问题,主要考察选手对于状态设计以及状态转移的理解能力。该问题的核心在于通过合理的状态定义和高效的转移方程来解决问题。 --- #### 动态规划解法详解 ##### 1. **状态表示** 设 `dp[i][j]` 表示前 `i` 项构成的序列中,第 `i` 项为 `j` 的合法方案总数[^1]。 这里的关键是对状态的设计进行简化,减少不必要的维度以提高效率。 ##### 2. **状态计算(集合划分)** 为了满足题目中的约束条件,可以通过数学推导得出如下转移关系: ```plaintext dp[i][j] = (dp[i-1][c(j - a * i)] + dp[i-1][c(j + b * i)]) % MOD ``` 其中: - `c(x)` 表示对 `n` 取模后的结果; - 特别需要注意的是,在处理负数取模时应确保结果始终为正数[^2]。 上述公式的含义是当前状态可以从上一步两个可能的状态转移而来,即考虑加减两种操作分别对应的不同情况。 ##### 3. **初始化与边界条件** 初始状态下,当只有一项 (`i=1`) 时,每种数值都只有唯一一种可能性,因此可以设置为: ```plaintext dp[1][j] = 1 (对于所有有效的 j) ``` 同时还需要设定合适的范围限制以防止越界访问数组索引超出预定义大小的情况发生。 ##### 4. **优化方向** 由于直接实现可能会面临较高的时间复杂度 O(n*m),针对某些特定参数组合可尝试进一步剪枝或者利用矩阵快速幂加速求解过程[^3]。 --- #### AC代码实例 以下是基于以上分析编写的一个 Python 实现版本: ```python MOD = 10**9 + 7 def solve_wave_sequence(a, b, m, n): # 初始化 DP 数组 dp = [[0]*(m+1) for _ in range(n+1)] # 初始状态 for j in range(m+1): dp[1][j] = 1 # 定义辅助函数用于安全取模 def mod_safe(x, base=m): return ((x % base) + base) % base # 进行动态规划填充 for i in range(2, n+1): for j in range(m+1): prev1 = mod_safe(j - a*i) prev2 = mod_safe(j + b*i) if 0 <= prev1 <= m and 0 <= prev2 <= m: dp[i][j] = (dp[i-1][prev1] + dp[i-1][prev2]) % MOD result = sum(dp[n][j] for j in range(m+1)) % MOD return result # 测试数据输入部分省略... print(solve_wave_sequence(1, 2, 5, 3)) ``` 此代码片段实现了基本的功能需求并兼顾性能考量。 --- #### 注意事项 在实际编码过程中需特别留意以下几个方面: - 输入验证:确保所有变量均处于合理范围内。 - 边缘测试:检查极端情况下程序行为是否符合预期。 - 性能调优:如果发现超时现象,则重新审视算法逻辑是否存在冗余计算环节。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值