高中信息技术之递归题解(一)
1. 爬楼梯
爬一个 n 级楼梯,他一步可以最多爬 k 级楼梯,并且他每步至少爬一级台阶(他不能爬小数级台阶,也不能往回走)。
输入两个数 n 和 k ,你需要输出 n 个数,分别表示他走 1 步爬完楼梯的方案数,走 2 步爬完楼梯的方案数,⋯,走 n 步爬完楼梯的方案数。
ans = [0 for _ in range(1145)]
def solve(step, height): if(height > n): return elif(height == n): ans[step] += 1 return else: for i in range(1, k+1): solve(step+1, height+i)
n, k = map(int, input().split())solve(0, 0)print(*ans[1:n+1])首先确定递归函数所要包含的状态:步数和爬到了第几级。然后确定边界条件:当超过了 height 时,即为到达终点,那么给当前步数的方案数加上一。最后确定起始条件:刚开始步数为 ,也是在第 个台阶。
2. 跷跷板
有 n 个人在玩翘翘板,你需要将这 n 个人分成两组,使他们的体重差最小。
2.1 常规做法
考虑到对于每个人,可以将其体重放到组A,也可以放到组B,那么我们就可以在每一个分支的时候进行两种情况的下一轮递归。代码如下:
g1 = []g2 = []ans = 11145141919810def dfs(step): global ans if(step==n): ans = min(ans, abs(sum(g2) - sum(g1))) else: g1.append(w[step]) dfs(step+1) g1.pop(-1) g2.append(w[step]) dfs(step+1) g2.pop(-1)
n = int(input())w = list(map(int, input().split(" ")))
dfs(0)print(ans)其中, g1 g2 为体重的列表。当步数到达 时即为到达终点,那么计算最小体重差;否则进行枚举,若选取其到组A,就放进去;然后考虑组B,但在考虑组B前,要先清除之前的状态,我们称之为回溯,先 pop 掉之前数组里的内容,然后再进行新的枚举。
2.2 二进制枚举
对于这类 0/1 问题,我们还可以使用二进制枚举,使得空间更小且时间复杂度常数更小。
bi = 0n = int(input())a = list(map(int, input().split()))ans = 1145141919810
while bi < (1 << (n + 1)): su = [0, 0] for i in range(n): stat = 1 & (bi >> i) su[stat] += a[i] ans = min(abs(su[0] - su[1]), ans) bi += 1print(ans)对于每一个二进制位,它表示的是组A或组B,然后进行计算。
3. 数的拆分
任何一个大于 1 的自然数 n,总可以拆分成若干个小于 n 的自然数之和。
现在给你一个自然数 n,要求你求出 n 拆分成一些数字的和的方式。
假设当前 dfs 传进来一个要拆分的数字。那么,我们可以考虑先从这个大数中分离出一个小数出来。显然,这个小数的范围为 。又因为我们需要将数字从小到大排列,那么在枚举拆出来的小数的时候,从已拆出数列表的最后一个开始枚举起即可。
n = int(input())arr = []def dfs(step): if(sum(arr)==n): for i in range(len(arr)): print(arr[i], end="+" if i != len(arr)-1 else "\n") elif(sum(arr)>n): return else: if(not arr): for i in range(1, n): arr.append(i) dfs(step+1) arr.pop(-1) else: for i in range(arr[-1], n - sum(arr) + 1): arr.append(i) dfs(step+1) arr.pop(-1)dfs(0)4. 全排列
按照字典序从小到大输出长度为 n 的全排列。
首先生成一个从 的数列,然后新引入一个标记是否用过的数组 used ,如果已经使用过,那么在下一轮递归的时候就不考虑再使用这个数字了。
n = int(input())arr = []used = [False for i in range(n+1)]def dfs(step): if(len(arr)==n): print(*arr) else: for i in range(1, n+1): if(not used[i]): used[i] = True arr.append(i) dfs(step+1) used[i] = False arr.pop(-1)dfs(0)5. 走迷宫
斯宝走进了另一个迷宫。
这个迷宫共有 n×m 个格子,第 i 行第 j 个格子用 (i,j) 表示。
每个格子共有两个状态:
.表示这个位置是个空地,可以行走。#表示这个位置是堵墙,不能行走,也不能穿过。
斯宝每次可以从一个位置,向上下左右四个方向走一格,但是不能跃出迷宫外。具体的,如果她在 (i,j) ,那么她可以到达 (i−1,j),(i,j−1),(i+1,j),(i,j+1) 四个位置(如果四个位置都在迷宫内)。
斯宝一开始在 (1,1) 位置,她希望能走到 (n,m) ,可是她现在在迷宫里面,所以请求你帮助她求出任意一种,能到达 (n,m) 的方案。
如果没有可行的方案,输出 -1。
保证 (1,1) 和 (n,m) 两点为空地。
这是属于图的搜索,对于每一点,我们可以从上、下、左、右四个方向进行下一轮递归。递归时注意边界,不能越界;同时保存路径,在回溯时及时弹出旧的废弃路径。
inp = input().split()n, m = int(inp[0]), int(inp[1])mp = []for i in range(n): s = input() mp.append([c for c in s])
# print(n, m, mp)
vis = [[False] * m for _ in range(n)]path = []Flag = False
def output(): print(len(path)) for i in path: print(i[0] + 1, i[1] + 1)
def dfs(x, y): path.append((x, y)) if x == n - 1 and y == m - 1: Flag = True output() exit(0) vis[x][y] = True dx = [-1, 0, 1, 0] dy = [0, 1, 0, -1] for i in range(4): nx = x + dx[i] ny = y + dy[i] if nx >= 0 and nx < n and ny >= 0 and ny < m and not vis [nx][ny] and mp[nx][ny] == '.': dfs(nx, ny) path.pop()
dfs(0, 0)
if not Flag: print(-1)支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
无穷大?