蓝桥杯 算法训练 摆动序列 的三种解法

 
  算法训练 摆动序列 
 
时间限制: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
方法一:DFS+判断
/*
dfs深搜+枚举判断
每一个数据可以多次使用,所以需要回溯。先用DFS深搜将可能的结果找出来,然后用if条件进行判断
当只有两个数的时候一定成立,直接加一就可以否则就要满足 :(data[t-2]-data[t-3])*(data[t-1]-data[t-3])<0
现在解释一下,为什么不需要从头开始将条件判断过来,只需要判断刚刚放进去的数能不能满足就可以。
当既不满足t==2又不满足 (data[t-2]-data[t-3])*(data[t-1]-data[t-3])<0条件时,我们直接返回了,没有进行下面的运算,
前面的不满足后面无论满足与否都不需要考虑。
接下来再解释一下为什么不需要判断t的值和n的值得关系。
当t=n+1;时,我们判断的还是前n个数,进入循环以后会发现所有的数都被标记了,没有可以使用的数,会被直接return回去 
*/
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int sum,n;
int data[1000]={0},book[1000]={0};
void bfs(int t){
	if(t>1){
		if(t==2){
            sum++;
        }
        else if((data[t-2]-data[t-3])*(data[t-1]-data[t-3])<0){//数组下标是从0开始的,所以要多减去一个1 
            sum++;
        }
        else return ;
	}
	for(int i = 1;i <= n;i++){
		if(book[i]==0){
			data[t] = i;//标记 
			book[i] = 1;//标记 
			bfs(t+1);
			book[i] = 0;//回溯 
		}
	}
	return ;
}
int main(){
	sum=0;
	scanf("%d",&n);
	bfs(0);
	printf("%d\n",sum); 
	return 0;
} 
方法二:找规律:
/*
仔细观察之后可以看出是一道规律题
首先,如果是1的话构不成摆动序列,而对于2来说是任意组成的符合条件的两个数都是构成摆动序列,
顺序问题是Cn2*2,对于3来说是从中选取三个数,第一个数确定以后,后面的可大可小两种情况,
对于奇数,第一个数一定是中间那个数,第二个数可以大,可以小,使后面的确定下来,
对于偶数来说,第一个数和第二个数一定是中间的两个数,这样就确定了整体的排列顺序,
即从n个数中选择2到n个数的总和*2即为答案,然后C(N,1)+C(n.2)+。。。+C(n,n)=2的n次方-1。 
*/

#include <stdio.h>
#include <math.h> 
int main()
{
    int k;
    scanf("%d", &k);
    printf("%d", (int)(pow(2, k) - k - 1) * 2);
    return 0;
} 
方法三:动态规划:
题目有点排列组合的意思,那么我们可以考虑能否使用动态规划来解决,
使用动态规划的第一步就是将表建立起来 
如下表所示:横坐标表示选取多少个数,纵坐标表示k的值,里面的值表示种类


2345678
22





362




41282



52020102


6304030122

742707042142
85611214011256162
仔细观察可以发现:
k的值和选取的数量相同时:只有两种,第一列的值为Cn2*2=n*(n-1);
 a[i][j] = a[i-1][j]+a[i-1][j-1]; 

代码如下:
/*
题目有点排列组合的意思,那么我们可以考虑能否使用动态规划来解决,
使用动态规划的第一步就是将表建立起来 
*/ 
#include<iostream>  
using namespace std;  
int main()  
{  
    int k;  
    int a[21][21];  
    long sum = 0;  
    cin>>k;  
    for(int i = 2; i <= k; i++){  
        a[i][i] = 2;  
        a[i][2] = i*(i-1);  
        for(int j = 3; j < i; j++){  
            a[i][j] = a[i-1][j]+a[i-1][j-1];  
        }  
    }  
    for(int i = 2; i <= k; i++){  
        sum += a[k][i];  
    }  
    cout<<sum;  
    return 0;     
}   





  • 23
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值