在刷题的过程中刷到了两道既能用递归来做,也能用递推来做的题目,但是每道题目都有时间和空间的限制,递推和递归并无明显的优劣,要结合题目要求具体分析,有时候用递推更快,无法用递归;有时用递归更快,无法用递推。
题目1:扩充序列
给定一个只包含一个数字 1 的序列 [1]。
现在,要对该序列进行 n−1 次扩充。
每次扩充时,首先要将序列本身添加到其自身的尾部,然后还要在两个序列中间插入还未使用过的最小正整数。
例如,序列 [1] 经过一次扩充后,得到序列 [1,2,1],再经过一次扩充后,得到序列 [1,2,1,3,1,2,1]。
现在,请你计算在经过 n−1 次扩充后,序列的第 k 个元素的值是多少?(元素从 1 开始编号)
输入格式
共一行,包含两个整数 n 和 k。
输出格式
输出一个整数,表示经过 n−1 次扩充后,序列的第 k 个元素的值。
数据范围
对于前三个测试点,1≤n,k≤10。
对于全部测试点,1≤n≤50,1≤k≤2n−1。
输入样例1:
3 2
输出样例1:
2
输入样例2:
4 8
输出样例2:
4
样例解释
对于样例 1,经过 2 次扩充,得到序列 [1,2,1,3,1,2,1],其第 2 个元素为 2。
对于样例 2,经过 3 次扩充,得到序列 [1,2,1,3,1,2,1,4,1,2,1,3,1,2,1],其第 8 个元素为 4。
#利用递推来做超时,因为扩充后的list太大了,导致list之间的加法复杂度太高,报错MemoryError
# n,k = map(int, input().split())
# x = [1]
# num = [2]
# for i in range(0,n):
# x = x + num
# x = x + x[0:-1]
# num[0] = num[0] + 1
# print(x[k-1])
# 只能用递归来做,自顶向下的思考,递归的次数最多只有50次,而且不需要保存列表
def f(n,k):
part = 2**(n-1) -1
if k == part + 1:
return n;
elif k <= part:
return f(n-1,k)
else:
return f(n-1, k-part-1)
n,k = map(int, input().split())
print(f(n,k))
题目2:递推数列
给定 a0,a1,以及 an=p×an−1+q×an−2 中的 p,q。
这里 n≥2。
求第 k 个数 ak 对 10000 的模。
输入格式
输入包括 5 个整数:a0、a1、p、q、k。
输出格式
第 k 个数 ak 对 10000 的模。
数据范围
1≤a0,a1,p,q,k≤10000
输入样例:
20 1 1 14 5
输出样例:
8359
# 递归做会超时,因为递归的次数太多了,比如k若=9999,需要递归9999次
a0,a1,p,q,k = map(int, input().split())
# 提前对10000取模的目的是减少计算量,变成10000以下的数进行计算,根据数学性质,先取模和最后取模不影响结果
a0 = a0 % 10000
a1 = a1 % 10000
p = p % 10000
q = q % 10000
def f(x):
if x == 0:
return a0
elif x == 1:
return a1
else:
return (p*f(x-1) + q*f(x-2))
res = f(k)
print(res)
# 只能用递推做,对于这个题来说,递推只是一个数组,所以用递推做更快,不会超时
# a0,a1,p,q,k = map(int, input().split())
# a = [0 for i in range(k + 1)]
# a[0] = a0 %10000
# a[1] = a1 %10000
# for k in range(2,k+1):
# a[k] = p*a[k-1] + q*a[k-2]
# print(a[k]%10000)