蓝桥杯省模拟赛题目 摆动序列-------如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。

蓝桥杯 摆动序列

题目

问题描述

如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 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。

题解

​ 这题如果直接暴力求解的话,估计只能够过50%的数据,所以还是得使用dp来进行求解。dp[i][j]表示第i位数时,最大数为m时共有多少个。然后根据题目要求:如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。所以我们每次处理都需要判断i的奇偶,这里使用i&1进行判断(有不懂的小伙伴可以百度一下),然后再进行相应的dp过程。
直接看代码,有详细讲解

第一行中,令 d[1][j]为:第1个数选择大于等于 j的数的方案总数。

从第二行开始:

​ 奇数行中,令 d[i][j]为:第i个数选择大于等于j的数时的方案总数。
​ 偶数行中,令 d[i][j]为:第i个数选择小于等于j的数时的方案总数。

即从第二行开始,如果行数为偶数行,那么我们当前可能的数目为:dp[i][j] = (dp[i-1][j+1] + dp[i][j-1]) % 10000;,如果为奇数行则:dp[i][j] = (dp[i-1][j-1] + dp[i][j+1]) % 10000;。

​ 然后这样的话,如果我们总的长度为奇数的话,那么就是dp[m][1],如果是偶数,则为dp[m][n]。

#include<bits/stdc++.h>
using namespace std;
int dp[1004][1004];
	
int main(){
	
	int m,n;
	cin>>m>>n;
	for(int i=1; i<=n; i++){//初始化为下一行i可以选择的值的数目   
		dp[1][i]=n-i+1; // 比如dp11 = 4,即是第一行可选4个 
	}
	//注意 !!!!!!!!11 
	//第几行代表序列中填的第几个数
	//在奇数行中,dp[i][j]含义为:第i个数选择大于等于j的数时的方案总数。
	//在偶数行中,dp[i][j]含义为:第i个数选择小于等于j的数时的方案总数。
	// 注意奇偶 奇偶 奇偶 
	
	for(int i=2;i<=m;i++){
		if(i&1){
		//奇数行,第i个数填大于等于j有多少种情况  
		//因为大于等于j的情况=等于j的情况 + 大于等于j+1的情况,
		//而大于等于j+1的情况=等于j的情况 + 大于等于j+2的情况
		//一直递推累加下去,这里我们从j取最大值开始,采用 倒序 动态记忆化叠加,思考/草稿一下为什么倒序,提示---记忆化  
		
			for(int j=n;j>=1;j--){
				dp[i][j]= (dp[i-1][j-1]+dp[i][j+1])%10000; //不要忘了题目方案总数大于10000后要取余 
				
				//第一轮理解,这一i奇数行 选择大于等于j的方案数 (这时j=n  第一轮)
				// = 上一行i-1偶数行中选择小于j(即小于等于j-1)的方案 与 最末尾最大的数j组合的方案数(即 dp[i-1][j-1]) +  dp[i][j+1]   (此时dp[i][j+1]=0 ,因为j=n,j+1不在1~n范围,所以为零) 
				
				//第二轮理解,这一i奇数行选择大于等于j的方案数 (这时j=n--  第二轮)
				// = 上一行i-1偶数行中选择小于等于j(即小于等于j-1)的方案  与j组合的方案数 (即 dp[i-1][j-1]) 加上  dp[i][j+1] (即 上一轮中这一奇数行j选择大于等于n的方案数) 
				//以此类推 
			}
		}
		
		else{//偶数行 ,第i个数填小于等于j有多少种情况  
		//因为小于等于j的情况=等于j的情况 + 小于等于j-1的情况,
		//而小于等于j-1的情况=等于j的情况 + 小于等于j-2的情况
		//一直递推累加下去,这里我们从j取最小值开始,采用 正序 动态记忆化叠加,思考/草稿一下为什么正序,提示---记忆化 
		
			for(int j=1;j<=n;j++){
				dp[i][j]=dp[i-1][j+1]+dp[i][j-1]; //不要忘了题目方案总数大于10000后要取余
				 
				//第一轮理解,这一i偶数行 选择小于等于j的方案数 (这时j=1  第一轮)
				// = 上一行i-1奇数行中选择大于j(即大于等于j+1)的方案 与 最开头最小的数j组合的方案数(即 dp[i-1][j+1]) +  dp[i][j-1]   (此时dp[i][j-1]=0 ,因为j=0,j-1不在1~n范围,所以为零) 
				
				//第二轮理解,这一i偶数行选择小于等于j的方案数 (这时j=1++  第二轮)
				// = 上一行i-1奇数行中选择大于j(即小于等于j+1)的方案 与 j组合的方案数 (即 dp[i-1][j+1]) 加上  dp[i][j-1] (即 上一轮中这一偶数行j选择小于等于1的方案数) 
				//以此类推 
			} 
		}
	}
	
	//最后,如果m长度为奇数,选择dp[m][1],  如果m长度为偶数,选择dp[m][n], 根据上面dp过程思考一下为什么 
	int ans = m & 1 ? dp[m][1] : dp[m][n];
	
	//输出 
    cout<<ans;
	return 0;
} 
有问题可以在下方留言哦~
  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值