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返回即可.第二个剪枝则是,我们可以将小猫的体重从大到小排序,这样我们的搜索树就会缩短许多。
其实有以下基本剪枝方法:
优化搜索顺序 ✔
就是换个搜索顺序,但效率不同,也就是优先搜枝条少的搜索子树
这里我们可以优先搜索质量重的猫,所以我们可以把所有的猫按重量从大到小排序
排除等效冗余 ✖
因为整棵搜索树都会搜到,所以不能排除等效冗余
可行性剪枝 ✔
如果当前位置放了一个猫就会超重,那么就不要放
最优化剪枝 ✔
如果当前答案不优于最优解,那么就不要搜