递归与递推练习

文章目录

题目来自 AcWing


92. 递归实现指数型枚举 | 原题链接

使用 AcWing 上的挑战模式做题,所以码风看上去比较乱。

 1n = int(input())
 2nums = list(range(1, n + 1))
 3
 4def dfs(current, nums):
 5    if len(nums) == 0:
 6        print(" ".join(current))
 7        return
 8
 9    dfs(current, nums[1:])
10    dfs(current + [str(nums[0])], nums[1:])
11
12dfs(list(), nums)

94. 递归实现排列型枚举 | 原题链接

全排列,一般做法:

 1n = int(input())
 2nums = {k: True for k in range(1, n + 1)}
 3result = list()
 4
 5def dfs(current):
 6   if not any(nums.values()):
 7       result.append(" ".join(current))
 8       return
 9
10   for i in nums.keys():
11       if nums[i]:
12           current.append(str(i))
13           nums[i] = False
14           dfs(current)
15           current.pop()
16           nums[i] = True
17
18dfs(list())
19result.sort()
20print(*result, sep="\n")

Python 取巧做法:

1from itertools import permutations
2
3n = int(input())
4perm = permutations(range(1, n + 1))
5perm = map(lambda x: " ".join(map(str, x)), perm)
6print(*sorted(perm), sep="\n")

717. 简单斐波那契 | 原题链接

 1n = int(input())
 2
 3if n == 1:
 4    print(0)
 5    exit(0)
 6
 7if n == 2:
 8    print(1)
 9    exit(0)
10
11fib = [0, 1]
12i = 2
13while i < n:
14    fib.append(fib[i - 1] + fib[i - 2])
15    i += 1
16
17print(" ".join(map(str, fib)))

95. 费解的开关 | 原题链接

这道题有点难住我了,最开始尝试广搜结果超时,深搜也超时,所以看了题解。题解中用的是模拟+递推的思想。
第一行的五个开关按与不按一共有 $2^5$ 种情况,对于每一种情况,都假设第一行开关的状态已经确定,如果此时要改变第一行灯的状态,则去按第二行的开关;当通过按第二行的开关将第一行的灯都打开后,第二行开关的状态已经确定,如果还需要更改第二行的灯的状态则去第三行……依此类推,直到第四行灯都打开而第五行开关状态确定时,检测第五行的灯是否全亮以及步数是否超过限制即可。

 1def toggle(field, x, y):
 2    field[x][y] = not field[x][y]
 3    for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
 4        nx, ny = x + dx, y + dy
 5        if 0 <= nx < 5 and 0 <= ny < 5:
 6            field[nx][ny] = not field[nx][ny]
 7
 8
 9n = int(input())
10for i in range(n):
11    field = list()
12    for _ in range(5):
13        row = list(map(lambda x: bool(int(x)), list(input().strip())))
14        field.append(row)
15    if i != n - 1:
16        input()
17
18    result = set()
19    for j in range(0, 32):
20        step = 0
21        new_field = [row[:] for row in field]
22        status = bin(j)[2:].rjust(5, "0")
23        for s in range(5):
24            if status[s] == "1":
25                toggle(new_field, 0, s)
26                step += 1
27        for x in range(1, 5):
28            for y in range(5):
29                if not new_field[x - 1][y]:
30                    toggle(new_field, x, y)
31                    step += 1
32        if step <= 6 and all(new_field[4]):
33            result.add(step)
34    if len(result) == 0:
35        print(-1)
36    else:
37        print(min(result))

93. 递归实现组合型枚举 | 原题链接

搜索与回溯解法:

 1def dfs(current, depth, k, nums):
 2    if depth == k:
 3        print(" ".join(current))
 4        return
 5
 6    for i in range(len(nums)):
 7        current.append(str(nums[i]))
 8        dfs(current, depth + 1, k, nums[i + 1:])
 9        current.pop()
10
11    return
12
13n, k = map(int, input().strip().split(" "))
14nums = list(range(1, n + 1))
15dfs(list(), 0, k, nums)

itertools.combinations 写法:

1from itertools import combinations
2
3n, k = map(int, input().strip().split(" "))
4comb = combinations(range(1, n + 1), k)
5for c in comb:
6    print(" ".join(map(str, c)))

1209. 带分数 | 原题链接

这道题大致的思路就是枚举 $a\space b\space c$ 的值,然后验证方程 $n=a+\frac bc$ 是否成立。对于这个题可以做如下优化:根据方程知道 $a<n$,所以枚举 $a$ 时,只需在位数小于等于 $n$ 的数字中枚举,且只有 $a<n$ 时才继续枚举另外两个变量;对原方程作变换后得到 $b=c\cdot(n-a)$,这样可以通过枚举 $a\space c$ 计算出 $b$,然后判断 $b$ 是否是由剩下的数字组合而成,而不需要再去枚举 $b$ 的值。

使用 Python 解决问题时,为了方便,使用 itertools 中的 combinationspermutations 函数。在枚举 $a\space c$ 时,首先确定位数,随后用 permutations 函数筛选出指定位数的各种组合,最后使用 combinations 函数生成对应的全排列,将每一种排列连接成数即是要枚举的数。

 1from itertools import permutations, combinations
 2
 3
 4n = input()
 5length = len(n)
 6n = int(n)
 7nums = list(range(1, 10))
 8count = 0
 9
10
11def get_c(a, rest):
12    for i in range(1, len(rest) - 1):
13        perm_c = permutations(rest, i)
14        for p in perm_c:
15            comb_c = combinations(p, i)
16            for comb in comb_c:
17                c = int("".join(map(str, comb)))
18                b = n * c - a * c
19                b_rest = set(rest) - set(comb)
20                if len(str(b)) != len(b_rest):
21                    continue
22                b_set = set(map(int, list(str(b))))
23                if b_rest == b_set:
24                    global count
25                    count += 1
26
27
28for i in range(1, length + 1):
29    perm_a = permutations(nums, i)
30    for p in perm_a:
31        comb_a = combinations(p, i)
32        for c in comb_a:
33            a = int("".join(map(str, c)))
34            if a < n:
35                rest = list(set(nums) - set(c))
36                get_c(a, rest)
37
38
39print(count)

116. 飞行员兄弟 | 原题链接

题目类似于费解的开关,想不到递推或者递归的写法,但是用广搜可以 AC。
因为最优解中肯定没有重复使用的开关,而且解与路径无关,因此给 16 个开关按照 0-15 编号,当一个开关被操作后,下一次操作只能在这个编号大于这个开关的开关中完成。

 1from collections import deque
 2
 3
 4def toggle(field, x, y):
 5    new_field = [row[:] for row in field]
 6    for i in range(4):
 7        new_field[x][i] = not field[x][i]
 8        new_field[i][y] = not field[i][y]
 9    new_field[x][y] = not field[x][y]
10    return new_field
11
12
13field = list()
14for _ in range(4):
15    row = [True if s == "-" else False for s in input().strip()]
16    field.append(row)
17
18queue = deque()
19step = list()
20queue.append((field, step))
21
22while queue:
23    field, step = queue.popleft()
24    if all([all(s) for s in field]):
25        print(len(step))
26        for s in step:
27            print(" ".join(map(lambda x: str(x + 1), s)))
28        exit()
29
30    if not step:
31        last_x, last_y = (0, -1)
32    else:
33        last_x, last_y = step[-1]
34    for i in range(last_x * 4 + last_y + 1, 16):
35        curr_x = i // 4
36        curr_y = i % 4
37        new_field = toggle(field, curr_x, curr_y)
38        new_step = step[:]
39        new_step.append((curr_x, curr_y))
40        queue.append((new_field, new_step))

1208. 翻硬币 | 原题链接

 1toggle = {"o": "*", "*": "o"}
 2
 3init = list(input().strip())
 4dest = list(input().strip())
 5
 6if init == dest:
 7    print(0)
 8    exit()
 9
10length = len(init)
11step = 0
12
13for i in range(length - 1):
14    if init[i] != dest[i]:
15        init[i] = toggle[init[i]]
16        init[i + 1] = toggle[init[i + 1]]
17        step += 1
18
19print(step)

相关系列文章