蓝桥杯 - 序列计数(记忆化搜索)

问题描述

  小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
  请计算,对于给定的 n,有多少种满足条件的序列。

输入格式

  输入一行包含一个整数 n。

输出格式

  输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。

样例输入

4

样例输出

7

样例说明

  以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4

评测用例规模与约定

  对于 20% 的评测用例,1 <= n <= 5;
  对于 50% 的评测用例,1 <= n <= 10;
  对于 80% 的评测用例,1 <= n <= 100;
  对于所有评测用例,1 <= n <= 1000。

题目分析:模拟赛的时候看到n很小,并且自己也只会n^3的记忆化搜索,就直接暴力打了个表交上去了,因为题目强调了当前项和上一项之间的关系,所以 dp[ i ][ j ] 代表的就是前一项为 i ,当前项为 j 时的方案数,这样在dfs里再套一层for就很简单的写出来了,n^3的方法就不多说了

重点是如何优化为 n^2 的算法,因为 dp[ i ][ j ] 的两维是无法优化的了,可以着手考虑的是每次dfs里的那一层for循环能否优化掉,这里就可以借助前缀和的思想了,将 dp[ i ][ j ] 所表示的意义转换为:前一项为 i ,当前项为 [ 1 , j ] 时的方案数,这样最后的答案从先前的\sum_{i<=n}^{i=1}dp[n][i]变为了 dp[ n ][ n ] ,转移方程也比较巧妙(我自己反正推不出来):

dp[ i ][ j ] = dp[ i ][ j - 1 ] + dp[ j ][ abs( i - j ) - 1 ] + 1

如何理解呢,因为 dp[ i ][ j ] 的意义转换为了前缀和的思想,所以 dp[ i ][ j ] 在前一项固定的基础上,应该是从当前项为 j - 1 时转移而来,加上 前一项为 i ,当前项为 j 时的方案数就是 dp[ i ][ j ] 了,最后加上一是因为本身对答案也有贡献,而前一项为 i ,当前项为 j 的答案,就可以利用题目给出的绝对值之差这个条件约束了,妙啊

动态规划的题目一般都是只可意会不可言谈。。

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
//#include<set>
#include<sstream>
using namespace std;
      
typedef long long LL;

typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
 
const int N=1e3+100;

const int mod=10000;

int dp[N][N];

int dfs(int pre,int cur)
{
	if(cur<=0)
		return 0;
	if(dp[pre][cur]!=-1)
		return dp[pre][cur];
	return dp[pre][cur]=(1+dfs(pre,cur-1)+dfs(cur,abs(pre-cur)-1))%mod;
}

int main()
{
#ifndef ONLINE_JUDGE
//	freopen("input.txt","r",stdin);
//	freopen("output.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	memset(dp,-1,sizeof(dp));
	int n;
	scanf("%d",&n);
	printf("%d\n",dfs(n,n));
	
	
	
	
	
	
	
	
	
	
	
	
	
	
    return 0;
}
发布了701 篇原创文章 · 获赞 29 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/105109419
今日推荐