递推/ 递归 / DFS


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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值