Python模板DFS
通过做题,让自己对模板有深刻的理解,同时在Python中因为默认的递归深度只有1000,我们可以通过下面操作增加递归深度(否则可能报错提示“maximum recursion depth exceeded incomparison”):
import sys
sys.setrecursionlimit(50000) #设置递归深度为50000
- 其他参数:一般根据题目来进行思考。
- 剪枝:通过对一些不必要继续递归的分支,或者提前找到答案之后进行return
- 标记状态和恢复状态:每次进行一个新的选择先标记已走过,知道走完不能走了,开始回溯回来之后,再把它恢复成未走过的标记。
常用来解决那些类型的问题
主要操作
- 树深度
- 子树节点数
- 中序、前序、后序输出
- 自定义排序
- 检查连通性
常见的DFS优化方式(剪枝)
- 可行性剪枝:对当前状态进行检查,如果当前条件不合法就不再继续,直接返回。
- 搜索顺序剪枝:搜索树有多个层次和分支,不同的搜索顺序会产生不同的搜索树形态。
- 最优性剪枝:在最优化问题的搜索过程中,如果当前花费的代价已超过前面搜索到的最优解,那么本次搜索已经没有继续进行下去的意义,停止对当前分支的搜索。
- 排除等效冗余:搜索的不同分支,最后的结果是一样的,那么只搜一个分支就够了。
- 记忆化搜索:在递归的过程中,有许多分支被反复计算,会大大降低算法的执行效率。将已经计算出来的结果保存起来,以后需要用到的时候直接取出结果,避免重复运算,从而提高了算法的效率。
题目的思考方式(参数的设置)
用一题经典的DFS的题目进行讲解,思考过程直接写在注释里面了
# -*- coding: utf-8 -*-
"""
@author: zjh
@time: 2023-04-05 15:05
"""
m, n = map(int, input().split())
mp = [list(map(int, input().split())) for i in range(n)]
vis = [[0] * m for j in range(n)] #用于标记是否走过
dir_list = [(-1, 0), (1, 0), (0, 1), (0, -1)] #四个方向的列表定义
"""
思考过程:
1.题目要求左上角的区域和为sum/2,另一半其他的格子也是sum/2,同时问最小的格子数是多少
2.通过题目要求分析dfs需要的参数
2-1 首先需要尝试各个路径,所以我们需要知道自己的坐标 需要x,y
2-2 又因为我们需要时刻知道自己目前的和有没有到达目标(sum/2),所以我们用一个current_sum 方便我们记录当前已经累加的和
2-3 最后题目问我们最小格子数,所以我们需要知道尝试的时候目前走了多少格子 使用cnt进行记录
3.是否有优化空间?
3-1 一条路径如果已经超过sum/2了,我们就没有必要继续走下去了 提前return
"""
# 矩阵所有元素的和
martix_sum = 0
for i in mp:
martix_sum += sum(i)
# 记录最小格子数
ans = float('inf')
def dfs(x, y, current_sum, cnt):
global martix_sum, ans
if current_sum > martix_sum / 2:
return
# 要包含左上角的第一个格子
if current_sum == martix_sum / 2:
if cnt < ans and vis[0][0] == 1:
ans = cnt
return
# 进入之后标记已走过
vis[x][y] = 1
for u, v in dir_list:
tx, ty = x + u, v + y
if 0 <= tx < n and 0 <= ty < m:
if vis[tx][ty] == 0:
#这边用的是current_sum+mp[x][y]是因为下次去的找路径的时候带的是前面算的和,
#所以不能是mp[tx][ty]
dfs(tx, ty, current_sum + mp[x][y], cnt + 1)
# 恢复原样
vis[x][y] = 0
dfs(0, 0, 0, 0)
print(ans)