波动数列(数论推导,dp)

本文介绍了波动数列的性质,以及如何利用动态规划解决关于此类数列的问题。具体涉及输入输出格式、样例解释、算法分析和代码实现。通过建立同余方程并运用动态规划的状态转移方程,求解满足条件的整数数列的方案数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

波动数列

题目链接
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?

输入格式

共一行,包含四个整数 n,s,a,b,含义如前面所述。

输出格式

共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 100000007 的余数。

数据范围

1≤n≤1000,
−109≤s≤109,
1≤a,b≤106

输入样例:

4 10 2 3

输出样例:

2
样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。

算法分析

和s == x + (x+d1)+(x+d1+d2)+…+(x+d1+…dn-1) 其中d∈{a,-b};
整理式子得 s == n * x + (n-1) * d1 +(n-2) * d2+…+dn-1;
s给了,n给了,a b给了所以d也是知道的,那么就要求x的可能数了,我们继续转化
x == {s -((n-1) * d1 +(n-2) *d2+…+dn-1)} /n;
那么x是整数,所以{s -((n-1) * d1 +(n-2) * d2+…+dn-1)} / n得到的是整数就意味着
s -((n-1) * d1 +(n-2) * d2+…+dn-1)是n的倍数,所以两个分式余n的余数是要相同的,这样可以使n整除
s%n是直接知道的,那么我们就要求((n-1) * d1 +(n-2)*d2+…+dn-1)%n了.这些都是一步一步来的,所以我们用动态规划的思想来进行
dp[i][j]表示前i项的和余n的余数为j
我们定义sum为前i-1项的和,第i项为i * a
我们能得到 (sum+(i * a))%n == j .j是小于n的所以j%n == j这样我们转换为同余方程
同余方程是可以直接加减的
(sum+(i * a))%n == j%n =>sum%n == (j-(i * a))%n.
所以我们能得到状态转移方程dp[i][j]=dp[i-1][(j-(i * a))%n]+dp[i-1][(j+(i * b))%n];
其中是有负数的所以我们要保证余数为正,所以加上一个方程就行了(a%b+b)%b就可以保证余数为正

代码实现

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3+5;
const int mod=100000007;
int dp[maxn][maxn];
int n,s,a,b;
int get_mod(int a,int b)
{
    return (a%b+b)%b;
}
int main()
{
    cin>>n>>s>>a>>b;
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<n;j++)
        {
            dp[i][j]=(dp[i-1][get_mod(j-a*i,n)]+dp[i-1][get_mod(j+b*i,n)])%mod;
        }
    }
    cout<<dp[n-1][get_mod(s,n)]<<endl;
    return 0;
}
<think>好的,我现在需要帮用户整理关于蓝桥杯Java B组的经典算法与模型总结。首先,我得回想一下蓝桥杯常见的题型和考点。用户提到的是Java B组,所以可能需要侧重于Java实现的一些算法,同时要结合蓝桥杯历年真题中的高频问题。 首先,蓝桥杯的题目通常涉及基础算法,比如排序、搜索、动态规划、贪心算法等。比如用户提供的引用中提到了使用Comparator进行排序,这可能涉及到自定义排序规则,这在算法题中很常见。另外,贪心算法在引用[2]中被提到,虽然适用情况少,但在某些问题如最大最小公倍数中可能用到。 接下来,动态规划是蓝桥杯中的重点,比如背包问题、最长子序列、路径问题等。需要总结常见的状态转移方程和Java实现方式。比如斐波那契数列、爬楼梯问题等基础DP,以及更复杂的二维或三维DP。 然后是图论算法,比如DFS、BFS、最短路径(Dijkstra、Floyd)、最小生成树(Prim、Kruskal)。这些在迷宫问题、网络连接等问题中经常出现。需要给出Java的实现示例,比如邻接表或矩阵的表示方法。 数论与数学问题也是常见的,比如质数判断、最大公约数、快速幂、排列组合等。引用[2]中的最小公倍数问题就是一个例子,可能需要用到数论知识。同时,快速幂算法在处理大数幂运算时很重要,比如模幂计算。 字符串处理方面,KMP算法、回文判断、字符串匹配等都是考点。比如,判断一个字符串是否为回文,或者子串查找问题。Java中字符串操作的API需要熟悉,但有时也需要手写算法。 搜索与回溯算法,比如全排列、N皇后、组合总和等,通常需要递归或迭代实现。这类题目考察对回溯剪枝的掌握,Java的实现需要注意递归的终止条件和状态恢复。 贪心算法虽然适用情况少,但像区间调度、背包问题的部分情况可能用到。需要明确贪心策略的适用条件,比如活动选择问题中的最早结束时间策略。 数据结构方面,栈、队列、堆、并查集、树状数组等都可能涉及。比如,使用优先队列(堆)来解决Top K问题,或者并查集处理连通性问题。 接下来,我需要考虑如何将这些内容结构化,分点整理,每个部分给出简要说明和示例代码。同时,要确保符合用户的要求,如行内数学公式用$...$,独立公式用$$...$$,并且正确使用LaTeX语法。比如动态规划中的状态转移方程可能需要独立公式。 另外,用户提供的引用[1]是关于使用Comparator进行排序的,这在Java中是一个常见的技巧,特别是在自定义对象排序时。需要提到如何通过实现Comparator接口来定义排序规则,并举例说明,比如在快速排序中的分区策略或者特定规则的排序问题。 引用[2]提到贪心算法在最小公倍数问题中的应用,这可能涉及到数论中的最大公约数和最小公倍数的关系,即$lcm(a,b) = \frac{a \times b}{gcd(a,b)}$。所以在数论部分需要强调这一点,并给出Java中求gcd的方法,如欧几里得算法。 同时,要注意生成相关问题,这些问题需要围绕总结的各个算法点展开,比如动态规划的应用场景、DFS与BFS的区别、如何优化递归算法等。这样可以帮助用户进一步深入理解每个部分。 最后,检查是否符合所有系统级指令,特别是格式要求和引用标注。确保每个引用在回答中正确标注,比如在提到排序时引用[^1],在贪心算法部分引用[^2]。 现在,我需要将这些思考整理成一个结构清晰、分点明确的总结,涵盖各个经典算法和模型,并附上相关的Java代码示例,同时生成相关问题供用户进一步学习。</think>### 蓝桥杯Java B组经典算法与模型总结 #### 一、基础算法 1. **排序与查找** - **快速排序**:常用于高效排序,需掌握分治思想。Java中可通过自定义`Comparator`实现复杂排序规则[^1]。 ```java Arrays.sort(arr, (a, b) -> a - b); // 自定义Comparator ``` - **二分查找**:时间复杂度$O(\log n)$,适用于有序数组。 ```java int index = Arrays.binarySearch(sortedArr, target); ``` 2. **贪心算法** - **适用场景**:局部最优解能推导全局最优解的问题,如区间调度、部分背包问题。 - **示例**:求最大最小公倍数时,贪心选择最大三个数: $$ \text{lcm}(a,b,c) = \frac{a \times b \times c}{\gcd(a,b,c)} $$ --- #### 二、动态规划DP) 1. **经典模型** - **背包问题**: - 0-1背包:状态转移方程: $$ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w_i] + v_i) $$ - 完全背包:优化为一维数组,正向遍历。 - **最长公共子序列(LCS)**: $$ dp[i][j] = \begin{cases} dp[i-1][j-1] + 1, & \text{if } s1[i] = s2[j] \\ \max(dp[i-1][j], dp[i][j-1]), & \text{otherwise} \end{cases} $$ 2. **Java实现示例** ```java int[][] dp = new int[n+1][m+1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s1.charAt(i-1) == s2.charAt(j-1)) { dp[i][j] = dp[i-1][j-1] + 1; } else { dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); } } } ``` --- #### 三、图论算法 1. **DFS与BFS** - **DFS**:递归或栈实现,用于路径搜索、连通性问题。 - **BFS**:队列实现,求最短路径(无权图)。 ```java Queue<Integer> queue = new LinkedList<>(); queue.offer(start); while (!queue.isEmpty()) { int node = queue.poll(); // 处理相邻节点 } ``` 2. **最短路径算法** - **Dijkstra**:适用于非负权图,优先队列优化: $$ \text{时间复杂度 } O((V+E)\log V) $$ ```java PriorityQueue<Node> pq = new PriorityQueue<>(Comparator.comparingInt(n -> n.dist)); ``` --- #### 四、数论与数学 1. **质数判断** - **试除法**:时间复杂度$O(\sqrt{n})$。 ```java boolean isPrime(int n) { if (n <= 1) return false; for (int i = 2; i * i <= n; i++) { if (n % i == 0) return false; } return true; } ``` 2. **最大公约数(GCD)** - **欧几里得算法**:递归或迭代实现。 $$ \gcd(a, b) = \gcd(b, a \% b) $$ ```java int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } ``` --- #### 五、字符串处理 1. **KMP算法** - **核心思想**:部分匹配表(PMT)跳过无效匹配。 ```java int[] buildPMT(String pattern) { int[] pmt = new int[pattern.length()]; int j = 0; for (int i = 1; i < pattern.length(); i++) { while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) { j = pmt[j-1]; } if (pattern.charAt(i) == pattern.charAt(j)) j++; pmt[i] = j; } return pmt; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值