Hongbo

Jul 17, 2020

蓝桥杯1

蓝桥杯快来了,给大家分享一道蓝桥杯的题目

题目

小明想知道,满足以下条件的正整数序列的数量

  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。

题解基本思路(看不懂的可以直接看Summarize)

  • 首先从样例输出来看,该序列的长度至少为2
  • 用递归枚举的方法来做这道题肯定是很耗时的,所以采用一点动态规划的思想
  • First, 先考虑长度为2的序列, n1 n2;类似于 1 1,2 1, 2 2这样。
  • Second, 我们考虑长度为3的序列,从样例来看,类似于 4 1 1, 4 2 1,4 1 2 这样的序列;我们只需观察该序列中的2,3两位,你会发现有熟悉 1 1, 2 1,他们属于我们n=1,n=2中符合条件的序列,也就是First中的情况;但是,你也会发现这里误入了一个 1 2,它似乎就让我们有点陌生了。
  • Then,我们在考虑长度为4的序列… 哈哈哈,皮一下,其实我们考虑以上两种情况就足以了。
Summarize
  • 刚刚只是给大家引了下路,这里给大家举个例子,对于给定的数字n,我用f(a,b)表示序列以a,b开头的序列的个数,如f(1,1)=1, f(4,1)=3,这个可以简单的笔算出来。
  • 规律就是,如果我们想知道所有 以5 2 …开头的序列的个数,我们是不是只需要知道以2 1, 2,2开头的序列的个数和就行了呢?(因为从第三项开始,每一项小于前两项的差的绝对值)而要知道 2 1, 2,2开头序列的个数,就可以从第一步轻松得到;即使上升一个较大的数值n,我们也能够递推至最小的子问题,只要我们将每种不同开头的个数储存起来,就能避免很多的重复运算,接着往下看
  • 在用二维数组处理时,我们假设 1 2( 1 4)这样特殊的”开头”的序列存在,他们用来储存 n3<n2 时对应序列的值,这样就可以和上面统一了。对于如何得到这个值,如求1 4 这样开头的序列,我们只需在多处理一步,就又变成了4 1, 4 2这样我们熟悉的处理了。

python代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def list_count(n):  
'''
动态规划: 构建 (n+1)*(n+1)整形矩阵
buf[i][j]: (i>=1, j>=1) 表示 i,j..开头的序列
4 1 2 3
n1 n2 n3 n4
由题:n1 > n2, n3<|n1 -n2|
假设,如果将n1去掉后:n2,n3,n4 也满足要求
此时存在情况:
1:
n2=n3, buf[n1][n2]=1
2:
n2>n3, buf[n1][n2]=buf[n2][1]+...+buf[n2][ni] (1<i<n2-n3-1)
3:
n2<n3,取|n2-n3|, buf[n1][n2]=buf[n3][1]+...+buf[n3][ni] (1<i<|n2-n3|)
'''

buf=[[0 for _ in range(0,n+1)] for _ in range(0,n+1)]
for i in range(1,n+1):
for j in range(1,i+1):
#主处理
if i-j>1:
buf[i][j]+=1
for k in range(1,i-j):
buf[i][j]+=buf[j][k]
else:
buf[i][j]+=1
buf[i][j]%=10000
#辅助处理
for x in range(1,i):
buf[x][i]+=1
for y in range(1, abs(x-i)):
buf[x][i]+=buf[i][y]

return sum(buf[n])%10000

如图:n=5时,buf的值为:

此方阵可以得到所有i<=n的结果,对buf[i]求和

OLDER > < NEWER