[蓝桥杯][算法训练VIP]摆动序列(深搜+回溯||动态规划)

题目描述
如果一个序列满足下面的性质,我们就将它称为摆动序列:

  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+回溯试了一下,发现数列收敛的很快,没有特别长的那种,所以时间复杂度是允许的(k<=20)。
    代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=2e1+1;
int a[maxx],vis[maxx];
int n;

inline void dfs(int num,int &ans)
{
	if(num>n) return ;
	if(num>=2)
	{
		ans++;
		if(a[num]>a[num-1]) 
		{
			for(int i=a[num-1]-1;i>=1;i--)
			{
				if(vis[i]==0)
				{
					a[num+1]=i;
					vis[i]=1;
					dfs(num+1,ans);
					vis[i]=0;
					a[num+1]=i;
				}
			}
		}
		else
		{
			for(int i=a[num-1]+1;i<=n;i++)
			{
				if(vis[i]==0)
				{
					a[num+1]=i;
					vis[i]=1;
					dfs(num+1,ans);
					vis[i]=0;
					a[num+1]=0;
				}
			}
		}
	}
	else
	{
		for(int i=a[num]-1;i>=1;i--)
		{
			if(vis[i]==0)
			{
				a[num+1]=i;
				vis[i]=1;
				dfs(num+1,ans);
				vis[i]=0;
				a[num+1]=0;
			}
		}
		for(int i=a[num]+1;i<=n;i++)
		{
			if(vis[i]==0)
			{
				a[num+1]=i;
				vis[i]=1;
				dfs(num+1,ans);
				vis[i]=0;
				a[num+1]=0;
			}
		}
	}
}
int main()
{
	scanf("%d",&n);
	int ans=0;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		a[1]=i;
		vis[i]=1;
		dfs(1,ans);
		vis[i]=0;
	}
	cout<<ans<<endl;
	return 0;
}

如果k再大一点,dfs就不是那么好了,因为回溯耗时也很大。那么我们考虑dp的方式。我们可以根据dfs的结果,建立一个表,在里面寻找规律。如下图所示(图片来源):
在这里插入图片描述
纵坐标代表的是k的取值,横坐标代表的是在这k个数中选取的数的个数。
我们可以发现,横坐标为2的时候,答案总是(k-1)*k。横坐标再大的时候,我们就可以发现规律了。
状态转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j-1].
代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=2e2+100;
int dp[maxx][maxx];
int n;

int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++) dp[i][2]=(i-1)*i;
	for(int j=3;j<=n;j++)
	{
		for(int i=3;i<=n;i++) dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
	}
	int ans=0;
	for(int i=2;i<=n;i++) ans+=dp[n][i];
	cout<<ans<<endl;
	return 0;
}

时间复杂度仅仅为O(n^2)。
努力加油a啊,(o)/~

发布了668 篇原创文章 · 获赞 118 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/starlet_kiss/article/details/105575262