题目信息:
在网上看到了许多题解,但好多人好像没有明白奇、偶状态转移方程怎么推导出来了,这是一道比较好的动态规划的题目,如果你找到现在也没有搞懂状态转移方程的由来,那么恭喜你这篇文章是个突破,看完这篇题目你就可以懂了!!!
(为了更好的理解,看这道题目之前,你可以先做一道初级的摆动序列的问题:链接: 动态规划详解——(初级)摆动序列.)
问题描述
如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]<a[2i-1], a[2i+1]>a[2i]。
小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。
输入格式
输入一行包含两个整数 m,n。
输出格式
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
样例输入
3 4
样例输出
14
样例说明
以下是符合要求的摆动序列:
2 1 2
2 1 3
2 1 4
3 1 2
3 1 3
3 1 4
3 2 3
3 2 4
4 1 2
4 1 3
4 1 4
4 2 3
4 2 4
4 3 4
评测用例规模与约定
对于 20% 的评测用例,1 <= n, m <= 5;
对于 50% 的评测用例,1 <= n, m <= 10;
对于 80% 的评测用例,1 <= n, m <= 100;
对于所有评测用例,1 <= n, m <= 1000。
动态规划分析:
-
状态分析:在这道题目中,数字在奇、偶位置不同所符合的条件不同,简而言之:奇数位置的数字要比前一位元素大、偶数位置的数字要比前一位小;这造就了状态转移方程的不同 -
状态:dp[i][j]状态数组表示:长度为j,即j位置上放以i数字为**基准(基准一词非常重要)**满足条件的序列的数目有多少个(这句话非常重要)- (eg:假设
j为奇数,长度为j的序列,则在j位置上放i数字满足条件,则在j位置上放数字i+1也满足条件,因为数字i都大于前面一位的数字了,数字i+1也一定大于前面的数字,所以满足条件) - (同理,eg:假设
j为偶数,长度为j的序列,则在j位置上放i数字满足条件,则在j位置上放数字i-1也满足条件,因为数字i小于前面一位的数字了,数字i-1也一定小于前面的数字,所以满足条件)
- (eg:假设
-
状态转移方程:j位置为奇数:当求dp[i][j]时,即长度为j,以i为基准其来源有两部分:dp[i+1][j](j位置放比i大的值)、dp[i-1][j-1](j位置放i,即j-1位要比i小)
简而言之——长度为j,大于等于i的选择方案总数j位置为偶数:当求dp[i][j]时,即长度为j,以i为基准其来源有两部分:dp[i-1][j](j位置放比i小的值)、dp[i+1][j-1](j位置放i,即j-1位要比i大)
简而言之——长度为j,小于等于i的选择方案总数
-
初始状态:i
当j为1时,即序列长度为1,那么其一定是满足条件的序列,第一项是奇数项,dp[i][1]=n-i+1; 大于等于j的选择方案总数 -
输出:
当m为奇数时,结合前面分析最后一位应当放最小值,即dp[1][m]
当m为偶数时,结合前面分析最后一位应当放最最大值值,即dp[n][m]
AC代码(java):
package 蓝桥杯模拟_摆动序列;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int m=sc.nextInt();
int n=sc.nextInt();
int[][] dp=new int[n+5][m+5]; //状态数组,长度为j,即j位置上放以i为基础满足条件的序列的数目有多少个
for(int i=1;i<=n;i++) {//初始化
dp[i][1]=n-i+1; //1为奇数位置
}
for(int j=2;j<=m;j++) {
if(j%2==0) {
for(int i=1;i<=n;i++) {
dp[i][j]=(dp[i+1][j-1]+dp[i-1][j])%10000; //以所放数字i为基础,考虑满足条件的序列
}
}
else {
for(int i=n;i>=1;i--) {
dp[i][j]=(dp[i+1][j]+dp[i-1][j-1])%10000; //以所放数字i为基础,考虑满足条件的序列
}
}
}
//判断调价输出
if(m%2==0) {
System.out.println(dp[n][m]);
}
else {
System.out.println(dp[1][m]);
}
}
}
4016

被折叠的 条评论
为什么被折叠?



