**
试题 算法训练 摆动序列
**
资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
如果一个序列满足下面的性质,我们就将它称为摆动序列:
1. 序列中的所有数都是不大于k的正整数;
2. 序列中至少有两个数。
3. 序列中的数两两不相等;
4. 如果第i – 1个数比第i – 2个数大,则第i个数比第i – 2个数小;如果第i – 1个数比第i – 2个数小,则第i个数比第i – 2个数大。
比如,当k = 3时,有下面几个这样的序列:
1 2
1 3
2 1
2 1 3
2 3
2 3 1
3 1
3 2
一共有8种,给定k,请求出满足上面要求的序列的个数。
输入格式
输入包含了一个整数k。(k<=20)
输出格式
输出一个整数,表示满足要求的序列个数。
样例输入
3
样例输出
8
前言:
博主大二菜鸟一枚,假期备考蓝桥杯,第一次写博客,希望能在博客中一起分享自己学到的东西,然后把学到的东西以自己理解的样式更通俗地分享给大家,有不足的地方大家多多指正,和大佬们多多学习!
思路与想法:
首先这道题的思想是 dp动态规划,所谓动态规划是求解决策过程最优化的过程,往往是直接算出答案,而不是得出每一步得过程。
这道题并不能用遍历的思想来思考,比如你打算遍历每一种情况的序列,然后if满足某种形式count++这种一定是不行的,数据太大会超时或效率太低并且很复杂
动态规划都会用到二维数组,并且需要多重的循环来解题,这道题中我们会用到一个二维数组dp[20][20]来储存结果,具体下面会讲到数组的含义
那么这道题怎么去入手呢?
1、从题干中我们可以知道:每个序列中的数字数量一定大于等于2,并且小于等于k
例:k = 3时,序列可以是数量为2的1 2,也可以是数量为3的1 3 2(序列中的最大值不能大于k且最少为两个数字)
2、那么我们可以去自己把k较小时的序列情况在纸上写出来,找一些规律和思路(这往往是一些算法题的精髓)
当k = 2时,只有数量为2的序列:12或 21两种
当k = 3时,数量为2的序列:12, 13, 21, 23, 31, 32六种
数量为3的序列有213,231两种
当k = 4时,数量为2的序列有12,13,14,21,23,24,31,32,34,41,42,43共12种
数量为3的序列有:213,214,314,324,231,241,341,342共8种
(我们这时不难发现小大小和大小大的情况数量一样)
数量为4的序列有:2314,3241两种(大小大小 和 小大小大各一种)
那么我们将这个结果总结为下图:
(纵坐标代表k的值,横坐标代表当前的k下,n个数字组成的序列的种数)
那么我们可以得出规律:
1、数字数量为2的序列一共有:k * (k - 1) 种
if(j == 2)
{
dp[i][j] = i * (i - 1);
}
2、对角线都是 2 种
else if(j == i)
{
dp[i][j] = 2;
}
3、其他情况下,dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
else
{
dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
}
4、我来解释一下二维数组dp[i][j]的含义,i代表k的值, j 代表序列中数字的个数
,那么我们这题的思路就是 把所有k情况下得结果都得出来,i = 2、3、4、5、6……,然后直接找到i == k时的dp[k][j]下每一个值相加再cout就好啦
for(int j=2;j<=k;j++)
{
answer+=dp[k][j]; //answer累加每一种的情况的数量
}
下面附完整代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include<string.h>
using namespace std;
int main( )
{
int k,ans=0;
int dp[21][21];
memset(dp,0,sizeof(dp)); //初始化数组
cin>>k;
int i,j,x;
for(i=2;i<=k;i++) // i : k=?的情况,最少两个数字组成序列,所以i=2开始,最大不能大于k
{
for(j=2;j<=i;j++) // j : 序列有多少个数字,最少是两个所以是j=2开始
{
if(j == 2) //两个数字的情况
{
dp[i][j] = i * (i - 1);
}
else if(j == i) ///对角线的情况
{
dp[i][j] = 2;
}
else ///其他情况的时候
{
dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
}
}
}
for(x=2;x<=k;x++) ///输出答案
{
ans+=dp[k][x]; //在k的情况下,相加每一种数量数字的种类
}
cout << ans; //输出答案
return 0;
}
执行结果:
验算:(52 == 20 + 20 + 10 + 2)