试题 算法训练 摆动序列(蓝桥杯 C/C++)

**

试题 算法训练 摆动序列

**

资源限制
时间限制: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)

后语:本题还有另外解法:贪心等,想了解的可以去其他大佬那里学习哈哈,第一次写博客肯定很多不足的地方希望大家发现问题了多多评判我会及时改正,有其他问题期待和大家一起交流!

猜你喜欢

转载自blog.csdn.net/Kyrie_irving_kun/article/details/113618572