DFS一些:连通性、顺序搜索、剪枝与优化

DFS之连通性模型

题目:1112. 迷宫 - AcWing题库

代码:

import sys
sys.setrecursionlimit(1000000)  # 设置递归深度限制为 1000000
N = 110
g = [[0] * N for _ in range(N)]  # 初始化二维列表 g,大小为 N*N,初始值为 0
dx = [0, 1, 0, -1]  # 定义方向数组 dx,表示上、右、下、左四个方向的行变化
dy = [1, 0, -1, 0]  # 定义方向数组 dy,表示上、右、下、左四个方向的列变化

def dfs(sx, sy):
    if g[sx][sy] == '#':  # 如果当前位置是障碍物 '#'
        return False
    if sx == c and sy == d:  # 如果当前位置是终点 (c, d)
        return True

    g[sx][sy] = '#'  # 将当前位置标记为已访问过

    # 尝试向四个方向进行搜索
    for i in range(4):
        x = sx + dx[i]
        y = sy + dy[i]

        if 0 <= x < n and 0 <= y < n and g[x][y] == '.':  # 确保新位置 (x, y) 在有效范围内且为可通行路径 '.'
            if dfs(x, y):  # 递归搜索下一个位置 (x, y)
                return True
    
    return False  # 所有方向都尝试过后未找到路径,返回 False

T = int(input())  # 读取测试用例数
while T:
    T -= 1

    n = int(input())  # 读取迷宫大小 n
    for i in range(n):
        g[i][:n] = list(input())  # 读取迷宫地图,存储到二维列表 g 中

    a, b, c, d = map(int, input().split())  # 读取起点 (a, b) 和终点 (c, d)
    if dfs(a, b):  # 调用深度优先搜索函数 dfs,判断能否从起点 (a, b) 到达终点 (c, d)
        print("YES")  # 能够找到路径,输出 "YES"
    else:
        print("NO")  # 找不到路径,输出 "NO"

说明:

关于连通性,其实最好的求解方式一般就是搜索。

也就是从头开始(从尾开始也行)搜索

这道题同样也有深搜和广搜两种方法。

DFS之搜索顺序

题目:1116. 马走日 - AcWing题库

代码:

t = int(input())  # 读取测试用例数
dx = (-2, -1, 1, 2, 2, 1, -1, -2)  # 骑士走法的横向位移
dy = (1, 2, 2, 1, -1, -2, -2, -1)  # 骑士走法的纵向位移
N = 10  # 棋盘大小
st = [[False]*N for _ in range(N)]  # 记录棋盘上每个位置是否被访问过的状态数组

def dfs(x0, y0, u):
    """
    dfs函数:从(x0, y0)开始,填满剩下的棋盘,有多少种方法。注意参数u的含义是树的深度
            当遍历到u*m层时,即棋盘最后一个格子时,返回1
    """
    if u == n * m:
        return 1  # 如果深度达到n*m,表示棋盘填满,返回1

    st[x0][y0] = True  # 标记当前位置为已访问

    ans = 0
    for i in range(8):
        x = x0 + dx[i]
        y = y0 + dy[i]
        if x < 0 or x >= n or y < 0 or y >= m or st[x][y]:
            continue  # 越界或者已经访问过的位置,跳过

        # 递归填充下一个位置
        ans += dfs(x, y, u + 1)

    st[x0][y0] = False  # 恢复当前位置为未访问,进行回溯

    return ans  # 返回当前位置的可填充方式数

# 处理每个测试用例
for _ in range(t):
    n, m, x, y = map(int, input().split())  # 读取棋盘大小n*m和起始位置(x, y)
    ans = dfs(x, y, 1)  # 调用dfs函数计算填充棋盘的方式数
    print(ans)  # 输出结果

DFS之剪枝与优化

题目:165. 小猫爬山 - AcWing题库

代码:

n, m = map(int, input().split())
w= []
for _ in range(n):
    w.append(int(input()))
w.sort(reverse=True) # 从大到小排,便于优化搜索
res = n # 初始化分配结果为最大的可能组数
cabins = [] # 存储当前分组的列表
def dfs(u):
    global res

    # 如果当前组数已经超过组数,进行剪枝
    if len(cabins) >= res:
        return
    # 如果遍历完了重量, 更新res
    if u == n:
        res = min(res, len(cabins))
        return
    for i in range(len(cabins)):
        if w[u] + cabins[i] <= m:
            cabins[i] += w[u]
            dfs(u+1) # 开始遍历下一个重量
            cabins[i] -= w[u] # 回溯,还原
    # 开一个新的组的情况
    cabins.append(w[u])
    dfs(u+1)
    cabins.pop() # 回溯,还原
dfs(0)
print(res)

说明:

我们发现这道题目有两个可以剪枝的部分,一个是如果当前的答案已经大于了我们已知的最小答案,不用说直接return返回即可.第二个剪枝则是,我们可以将小猫的体重从大到小排序,这样我们的搜索树就会缩短许多。

其实有以下基本剪枝方法:

优化搜索顺序 ✔
就是换个搜索顺序,但效率不同,也就是优先搜枝条少的搜索子树
这里我们可以优先搜索质量重的猫,所以我们可以把所有的猫按重量从大到小排序
排除等效冗余 ✖
因为整棵搜索树都会搜到,所以不能排除等效冗余
可行性剪枝 ✔
如果当前位置放了一个猫就会超重,那么就不要放
最优化剪枝 ✔
如果当前答案不优于最优解,那么就不要搜

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值