1.指数
92. 递归实现指数型枚举:从 1∼n 这 n 个整数中随机选取任意多个,可随意组合
递归搜索树(n=3)
如何搜索?
沿着外围
参考代码:
n = int(input())
def DFS(depth, path):
if depth == n:
if path:
# print(*path)
print(" ".join(map(str, path)))
else:
print()
return
# 不选
DFS(depth + 1, path)
# 选
DFS(depth + 1, path + [depth + 1])
DFS(0, [])
2.排列
P1706 全排列问题:求 1 ~ n 的全排列
n = int(input())
def DFS(path, used):
if len(path) == n:
for i in path:
print(f"{i: 5d}", end="")
print()
return
for i in range(1, n + 1):
if not used[i]: # i未被使用
used[i] = True
path.append(i)
DFS(path, used)
# 回溯
used[i] = False
path.pop()
DFS([], [False] * (n+1))
3.组合
P1157 组合的输出:从 n 个元素中抽出 r 个元素的所有组合
思路:依次枚举每个数放哪个位置
n, k = map(int, input().split())
def DFS(depth, path):
if len(path) == k:
for i in path:
print(f"{i: 3d}", end="")
print()
return
for i in range(depth, n):
path.append(i + 1)
DFS(i + 1, path)
path.pop()
DFS(0, [])
习题
P2089 烤鸡 指数
1088 火星人 全排列(90)
暴搜代码:(最后一个测试点超时)
import sys
sys.setrecursionlimit(10**6) # 增加递归深度限制
N = int(input()) # 火星人手指的数目
M = int(input()) # 要加上去的小整数
sequence = list(map(int, input().split())) # 火星人手指的排列顺序
# 思路:求1~5的全排列(字典序)(从火星数开始枚举),他的下标+1即为火星数,通过火星人手指的sequence找到数字的下标后直接+M
ans = 0 # ans为火星数
flag = False # 当得到结果后直接退出循环
def backtracking(path, used):
global ans, flag
if flag:
return
if len(path) == N: # 由于是从火星数开始枚举的,因此当ans == M + 1的时候,此时就是答案
ans += 1
if ans == M + 1:
flag = True
print(*path)
return
i = 0
while i < N: # for循环会使得剪枝无效,因此用while
# (剪枝)从sequence开始枚举:当没有任何方案的时候,直接让这个数变成sequence的数
if not ans:
i = sequence[len(path[:])] - 1
# 跳过被使用过的数
if used[i]:
i += 1
continue
used[i] = True
path.append(i + 1)
backtracking(path, used)
path.pop()
used[i] = False
i += 1
backtracking([], [False] * N)
ac代码:
import sys
import threading
def next_permutation(a):
n = len(a)
# Step 1: 从后向前找到第一个满足 a[i] < a[i+1] 的位置 i
i = n - 2
while i >= 0 and a[i] >= a[i + 1]:
i -= 1
if i == -1:
return False # 已经是最后一个排列
# Step 2: 从后向前找到第一个大于 a[i] 的元素 a[j]
j = n - 1
while a[j] <= a[i]:
j -= 1
# Step 3: 交换 a[i] 和 a[j]
a[i], a[j] = a[j], a[i]
# Step 4: 反转 a[i+1:] 的元素
a[i + 1:] = reversed(a[i + 1:])
return True
def run():
N = int(sys.stdin.readline())
M = int(sys.stdin.readline())
a = list(map(int, sys.stdin.readline().split()))
for _ in range(M):
has_next = next_permutation(a)
if not has_next:
# 如果没有下一个排列,说明已到达最后一个排列
# 但根据题意,这种情况不会发生
break
print(' '.join(map(str, a)))
threading.Thread(target=run).start()
P1149 火柴棒等式 预处理(60)
暴搜代码:
N = 1000
n = int(input()) # 火柴数
# arr = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6] # arr[][0]对应数字 arr[][1]对应其火柴数
arr = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6] + [0] * N
ans = 0 # 记录方案数
# 思路:遍历这些数,然后根据俩条件筛:1.「A+B=C」2.「A+B+C的火柴数为n-4」
# 遍历这些数:依次枚举这些位置可以选什么数(A B C < 10000)即从1~10000(try)这些数中选3个数进行排列
# 优化1:火柴棍是由参数直接传参进去保存的,方便后面的剪枝
# 优化2:arr数组下标对应数字,将arr数组开N个,下标对应相应的数,类似于DP将前面的状态保存
def DFS(depth, path, cnt):
global ans
# 剪枝
if cnt > n:
return
# 如果搜索深度==3终止
if depth == 3:
if path[0] > path[2] or path[1] > path[2]: # 剪枝
return
if path[0] + path[1] == path[2] and cnt == n - 4: # A+B=C and A+B+C的火柴数为n-4
ans += 1
return
for i in range(0, N):
path.append(i)
DFS(depth + 1, path, cnt + arr[i])
path.pop()
# 直接在arr数组中计算火柴数
for i in range(10, 1000):
arr[i] = arr[i % 10] + arr[i // 10] # 个位+十位/个位+十百位
DFS(0, [], 0)
print(ans)
ac代码:
def cal_sticks(n):
arr = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6] # 数字 0-9 对应的火柴棍数量
# 生成所有可能的数字及其对应的火柴棍数量
num_sticks = {}
for num in range(0, 1000): # 根据 n 的范围,数字最大不会超过 2000
s = str(num)
# 除了数字 0,其他数字不能以 '0' 开头
if len(s) > 1 and s[0] == '0':
continue
sticks = sum(arr[int(d)] for d in s)
if sticks > (n - 4):
continue
if sticks not in num_sticks:
num_sticks[sticks] = []
num_sticks[sticks].append(num)
cnt = 0
# 枚举所有可能的火柴棍数量组合 (sticks_A, sticks_B)
for sticks_A in num_sticks:
for sticks_B in num_sticks:
sticks_C = (n - 4) - sticks_A - sticks_B
if sticks_C not in num_sticks:
continue
# 枚举所有可能的数字组合 (A, B)
for A in num_sticks[sticks_A]:
for B in num_sticks[sticks_B]:
C = A + B
# 检查 C 是否在预处理的数字列表中
if C in num_sticks[sticks_C]:
# 数字 C 的合法性检查
s_C = str(C)
if len(s_C) > 1 and s_C[0] == '0':
continue
# 计数等式数量
cnt += 1
return cnt
n = int(input())
print(cal_sticks(n))
P2036 PERKET 指数
ac
P1135 奇怪的电梯 暴力
ac代码
from collections import deque
def min_steps_to_reach(N, A, B, K):
visited = [False] * (N + 1) # 记录每层是否被访问
queue = deque([(A, 0)]) # 队列存储 (当前楼层, 按键次数)
visited[A] = True
while queue:
current, steps = queue.popleft()
# 如果到达目标楼层,返回按键次数
if current == B:
return steps
# 尝试向上移动
up = current + K[current - 1]
if up <= N and not visited[up]:
queue.append((up, steps + 1))
visited[up] = True
# 尝试向下移动
down = current - K[current - 1]
if down >= 1 and not visited[down]:
queue.append((down, steps + 1))
visited[down] = True
# 如果无法到达目标楼层,返回 -1
return -1
# 输入处理
N, A, B = map(int, input().split())
K = list(map(int, input().split()))
# 输出结果
print(min_steps_to_reach(N, A, B, K))
迷宫问题
P1683 入门
ac
width, length = map(int, input().split())
matrix = []
for i in range(length):
li = list(input())
matrix.append(li)
# 思路:
# 先找到@入口,传坐标进DFS,然后依次访问上右下左四个方向
used = [[False for _ in range(width)] for _ in range(length)]
ans = 0
# 上右下左
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
# 传入坐标a b
def DFS(a, b):
global ans
for i in range(4):
x = a + dx[i]
y = b + dy[i]
if x < 0 or x >= length or y < 0 or y >= width:
continue
if matrix[x][y] != ".":
continue
if used[x][y]:
continue
used[x][y] = True
ans += 1
DFS(x, y)
for i in range(length):
for j in range(width):
if matrix[i][j] == "@":
used[i][j] = True
ans += 1
DFS(i, j)
print(ans)