DFS
例题
路径之谜



代码如下:
python
n = int(input())
north_target = list(map(int, input().split()))
west_target = list(map(int, input().split()))
#当前剩余箭数
north_rem = north_target[:]
west_rem = west_target[:]
dic = [(0, -1), (0, 1), (-1, 0), (1, 0)]
path = []
vis = [[False] * n for i in range(n)]
#可行性检查
def valid(x, y):
return 0 <= x < n and 0 <= y < n and not vis[x][y]
def dfs(x, y):
#递归出口:到达终点
if x == n - 1 and y == n - 1:
#检查所有靶是否用完
if all(x == 0 for x in north_rem) and all(y == 0 for y in west_rem):
return True
return False
for i in range(4):
xx, yy = x + dic[i][0], y + dic[i][1]
if valid(xx, yy):
if west_rem[xx] <= 0 or north_rem[yy] <= 0:
continue
#选择这个格子
west_rem[xx] -= 1
north_rem[yy] -= 1
vis[xx][yy] = True
path.append(xx * n + yy)
if dfs(xx, yy):
return True
#回溯
vis[xx][yy] = False
path.pop()
west_rem[xx] += 1
north_rem[yy] += 1
return False
vis[0][0] = True
north_rem[0] -= 1
west_rem[0] -= 1
path.append(0)
dfs(0, 0)
print(' '.join(map(str, path)))
全球变暖



代码如下:
python
import sys
sys.setrecursionlimit(10**6)
dic = [(0, 1), (0, -1), (1, 0), (-1, 0)]
def dfs(x, y):
vis[x][y] = 1
if mapp[x][y - 1] == '#' and mapp[x][y + 1] == '#' and mapp[x - 1][y] == '#' and mapp[x + 1][y] == '#':
global flag
flag = 1
for i in range(4):
xx, yy = x + dic[i][0], y + dic[i][1]
if mapp[xx][yy] == '#' and vis[xx][yy] == 0:
dfs(xx, yy)
n = int(input())
mapp = []
ans = 0
vis = [[0] * n for _ in range(n)]
for i in range(n):
mapp.append(list(input()))
for i in range(n):
for j in range(n):
if mapp[i][j] == '#' and vis[i][j] == 0:
flag = 0
dfs(i, j)
if flag == 0:
ans += 1
print(ans)
飞机降落


代码如下:
python
import sys
sys.setrecursionlimit(10 ** 6)
def can_schedule(planes, visited, current_time, count, n):
# 递归出口:所有飞机都已降落
if count == n:
return True
for i in range(n):
if not visited[i]:
T, D, L = planes[i]
# 最早可以开始降落的时间
start = max(current_time, T)
# 最晚可以开始降落的时间
latest = T + D
# 如果可以安排
if start <= latest:
visited[i] = True
if can_schedule(planes, visited, start + L, count + 1, n):
return True
visited[i] = False # 回溯
return False
def solve():
T = int(input().strip())
for _ in range(T):
n = int(input().strip())
planes = []
for _ in range(n):
t, d, l = map(int, input().split())
planes.append((t, d, l))
visited = [False] * n
if can_schedule(planes, visited, 0, 0, n):
print("YES")
else:
print("NO")
if __name__ == "__main__":
solve()
数字接龙


代码如下:
python
import sys
sys.setrecursionlimit(10**6)
dic = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]
# 存储已经连接过的线段
lines = []
def has_cross(x, y, xx, yy):
"""检查对角线移动是否会与已有路径交叉"""
# 右下
if xx == x + 1 and yy == y + 1:
line = {(x+1, y), (x, y+1)}
return line in lines
# 左下
elif xx == x + 1 and yy == y - 1:
line = {(x+1, y), (x, y-1)}
return line in lines
# 右上
elif xx == x - 1 and yy == y + 1:
line = {(x-1, y), (x, y+1)}
return line in lines
# 左上
elif xx == x - 1 and yy == y - 1:
line = {(x-1, y), (x, y-1)}
return line in lines
return False
def dfs(x, y, step):
if step == n * n:
if x == n - 1 and y == n - 1:
return True
return False
for i in range(8):
xx, yy = x + dic[i][0], y + dic[i][1]
# 边界检查
if not (0 <= xx < n and 0 <= yy < n):
continue
# 访问检查
if vis[xx][yy]:
continue
# 数字序列检查
if grid[xx][yy] != (grid[x][y] + 1) % k:
continue
# 对角线移动的交叉检查
if i in [1, 3, 5, 7]: # 1:右上, 3:右下, 5:左下, 7:左上
if has_cross(x, y, xx, yy):
continue
# 记录当前要走的线段(用于后续交叉判断)
line = {(x, y), (xx, yy)}
lines.append(line)
vis[xx][yy] = True
path.append(i)
if dfs(xx, yy, step + 1):
return True
# 回溯
path.pop()
vis[xx][yy] = False
lines.pop()
return False
n, k = map(int, input().split())
grid = []
path = []
for i in range(n):
grid.append(list(map(int, input().split())))
# 起点检查
if grid[0][0] != 0:
print("-1")
else:
vis = [[False] * n for _ in range(n)]
vis[0][0] = True
if dfs(0, 0, 1):
print("".join(map(str, path)))
else:
print("-1")
玩具蛇

代码如下:
python
dic = [(0, 1), (1, 0), (0, -1), (-1, 0)]
vis = [[False] * 4 for _ in range(4)]
ans = 0
def valid(x, y):
return 0 <= x < 4 and 0 <= y < 4 and not vis[x][y]
def dfs(x, y, count):
if count == 16:
global ans
ans += 1
return
for i in range(4):
xx, yy = x + dic[i][0], y + dic[i][1]
if valid(xx, yy):
vis[xx][yy] = True
dfs(xx, yy, count + 1)
vis[xx][yy] = False
for i in range(4):
for j in range(4):
vis[i][j] = True
dfs(i, j, 1)
vis[i][j] = False
print(ans)
|-------|--------------------------|---------------------|
| 特征 | 找一条路径 | 找所有路径/计数 |
| 函数返回值 | bool | void/None |
| 递归出口 | return True/False | return或return False |
| 递归调用 | if dfs(...): return True | dfs(...) |
| 找到解后 | 立即返回,停止搜索 | 计数,继续搜索 |
| 典型题目 | 路径之谜,飞机降落 | 玩具蛇,八皇后计数 |
迷宫


代码如下:
python
# 迷宫地图
maze = [
"UDDLUULRUL",
"UURLLLRRRU",
"RRUURLDLRD",
"RUDDDDUUUU",
"URUDLLRRUU",
"DURLRLDLRL",
"ULLURLLRDU",
"RDLULLRDDD",
"UUDDUDUDLL",
"ULRDLUURRR"
]
# 方向映射
dirs = {
'U': (-1, 0),
'D': (1, 0),
'L': (0, -1),
'R': (0, 1)
}
n = 10
# 状态数组:0未访问,1能走出,-1走不出
state = [[0] * n for _ in range(n)]
def dfs(x, y):
"""返回是否能走出迷宫"""
# 如果已经走出迷宫
if x < 0 or x >= n or y < 0 or y >= n:
return True
# 如果已经计算过
if state[x][y] != 0:
return state[x][y] == 1
# 临时标记为正在访问(防止循环)
state[x][y] = -1
# 获取当前格子的方向
direction = maze[x][y]
dx, dy = dirs[direction]
nx, ny = x + dx, y + dy
# 递归判断下一个格子
result = dfs(nx, ny)
# 记录结果
state[x][y] = 1 if result else -1
return result
# 统计能走出的人数
ans = 0
for i in range(n):
for j in range(n):
if dfs(i, j):
ans += 1
print(ans)
vis[x][y] = -1 的作用是:
-
防止循环递归:在环路中,第二次访问时能立即返回
-
作为临时状态:表示"正在计算中",避免重复计算
-
记忆化:记录已经计算过的点,提高效率
N皇后问题

代码如下:
python
def dfs(x):
if x == n + 1:
global ans
ans += 1
return
#尝试x行的每一列
for y in range(1, n + 1):
#如果列或对角线上已有皇后,跳过
if vis1[y] or vis2[x + y] or vis3[x - y + n]:
continue
#放置皇后,标记占用
vis1[y] = vis2[x + y] = vis3[x - y + n] = True
#递归放置下一行
dfs(x + 1)
#回溯
vis1[y] = vis2[x + y] = vis3[x - y + n] = False
n = int(input())
vis1 = [False] * (n + 1)#列占用情况,下标从1到n, (索引0闲置不用)
vis2 = [False] * (2 * n + 1)#(/方向)占用情况,覆盖从2到2n
vis3 = [False] * (2 * n + 1)#(\方向)
ans = 0
dfs(1)
print(ans)
模板
类型一:遍历/填充型 DFS(不需要回溯)
适用场景:连通分量、洪水填充、岛屿问题、拓扑排序
python
def dfs(x, y):
# 1. 标记当前节点已访问
visited[x][y] = True
# 2. 处理当前节点(可选)
# 例如:统计、判断、记录等
process_current(x, y)
# 3. 遍历所有相邻节点
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 4. 合法性检查
if not (0 <= nx < n and 0 <= ny < n):
continue
if visited[nx][ny]:
continue
if not is_valid(nx, ny): # 其他条件,如是否为陆地
continue
# 5. 递归搜索(不需要回溯)
dfs(nx, ny)
# 注意:这里没有回溯操作!
类型二:路径搜索型 DFS(需要回溯)
适用场景:迷宫路径、哈密顿路径、数独、八皇后、骑士巡游
python
def dfs(x, y):
# 1. 递归出口:到达目标状态
if (x, y) == (target_x, target_y):
if all_conditions_met():
return True
return False
# 2. 遍历所有可能的选择
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 3. 合法性检查
if not (0 <= nx < n and 0 <= ny < n):
continue
if visited[nx][ny]:
continue
if not check_constraints(nx, ny): # 其他约束
continue
# 4. 做出选择(记录状态)
visited[nx][ny] = True
path.append(get_id(nx, ny))
update_resources(nx, ny) # 如有资源约束
# 5. 递归搜索
if dfs(nx, ny):
return True
# 6. 撤销选择(回溯)
visited[nx][ny] = False
path.pop()
restore_resources(nx, ny) # 恢复资源
return False
# 7. 主函数中初始化起点
visited[start_x][start_y] = True
path.append(start_id)
init_resources(start_x, start_y)
dfs(start_x, start_y)
BFS
例题
迷宫

python
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
代码如下:
python
from collections import deque
# 迷宫数据(30行50列)
maze = [
"01010101001011001001010110010110100100001000101010",
"00001000100000101010010000100000001001100110100101",
"01111011010010001000001101001011100011000000010000",
"01000000001010100011010000101000001010101011001011",
"00011111000000101000010010100010100000101100000000",
"11001000110101000010101100011010011010101011110111",
"00011011010101001001001010000001000101001110000000",
"10100000101000100110101010111110011000010000111010",
"00111000001010100001100010000001000101001100001001",
"11000110100001110010001001010101010101010001101000",
"00010000100100000101001010101110100010101010000101",
"11100100101001001000010000010101010100100100010100",
"00000010000000101011001111010001100000101010100011",
"10101010011100001000011000010110011110110100001000",
"10101010100001101010100101000010100000111011101001",
"10000000101100010000101100101101001011100000000100",
"10101001000000010100100001000100000100011110101001",
"00101001010101101001010100011010101101110000110101",
"11001010000100001100000010100101000001000111000010",
"00001000110000110101101000000100101001001000011101",
"10100101000101000000001110110010110101101010100001",
"00101000010000110101010000100010001001000100010101",
"10100001000110010001000010101001010101011111010010",
"00000100101000000110010100101001000001000000000010",
"11010000001001110111001001000011101001011011101000",
"00000110100010001000100000001000011101000000110011",
"10101000101000100010001111100010101001010000001000",
"10000010100101001010110000000100101010001011101000",
"00111100001000010000000110111000000001000000001011",
"10000001100111010111010001000110111010101101111000"
]
n = 30 # 行数
m = 50 # 列数
# 方向顺序:D < L < R < U(字典序最小)
# 对应坐标变化:下、左、右、上
directions = [
(1, 0, 'D'), # 下
(0, -1, 'L'), # 左
(0, 1, 'R'), # 右
(-1, 0, 'U') # 上
]
def bfs():
# 队列:存储 (x, y)
queue = deque()
queue.append((0, 0))
# 记录路径:pre[x][y] = (上一个x, 上一个y, 方向字符)
pre = [[None] * m for _ in range(n)]
pre[0][0] = (-1, -1, '') # 起点的前驱
# 记录是否访问过
visited = [[False] * m for _ in range(n)]
visited[0][0] = True
while queue:
x, y = queue.popleft()
# 到达终点
if x == n-1 and y == m-1:
# 回溯路径
path = []
while pre[x][y][2] != '':
path.append(pre[x][y][2])
x, y = pre[x][y][0], pre[x][y][1]
# 逆序输出
return ''.join(path[::-1])
# 按字典序顺序探索四个方向
for dx, dy, direction in directions:
nx, ny = x + dx, y + dy
# 边界检查
if 0 <= nx < n and 0 <= ny < m:
# 检查是否可通行且未访问
if maze[nx][ny] == '0' and not visited[nx][ny]:
visited[nx][ny] = True
pre[nx][ny] = (x, y, direction)
queue.append((nx, ny))
return ""
result = bfs()
print(result)
走迷宫


代码如下:
python
from collections import deque
n, m = map(int, input().split())
maze = []
for i in range(n):
maze.append(list(map(int, input().split())))
x1, y1, x2, y2 = map(int, input().split())
x1, y1, x2, y2 = x1 - 1, y1 - 1, x2 - 1, y2 - 1
direction = [(0, -1), (0, 1), (-1, 0), (1, 0)]
def bfs():
queue = deque()
queue.append((x1, y1))
vis = [[False] * m for _ in range(n)]
vis[x1][y1] = True
distance = [[0] * m for _ in range(n)]
while queue:
x, y = queue.popleft()
if x == x2 and y == y2:
return distance[x][y]
for i in range(4):
xx, yy = x + direction[i][0], y + direction[i][1]
if 0 <= xx < n and 0 <= yy < m:
if maze[xx][yy] == 1 and not vis[xx][yy]:
vis[xx][yy] = True
distance[xx][yy] = distance[x][y] + 1
queue.append((xx, yy))
return -1
print(bfs())
模板
python
from collections import deque
def bfs():
# 1. 创建队列
queue = deque()
# 2. 起点入队
queue.append((start_x, start_y))
# 3. 标记起点已访问
visited[start_x][start_y] = True
# 4. 记录前驱(用于回溯路径)
pre = [[None] * m for _ in range(n)]
pre[start_x][start_y] = (-1, -1, '')
# 5. 方向数组(按字典序)
directions = [(1,0,'D'), (0,-1,'L'), (0,1,'R'), (-1,0,'U')]
# 6. 主循环:队列不为空就一直搜索
while queue:
# 取出队首元素
x, y = queue.popleft()
# 7. 到达终点
if x == end_x and y == end_y:
# 回溯路径
path = []
while pre[x][y][2] != '':
path.append(pre[x][y][2])
x, y = pre[x][y][0], pre[x][y][1]
return ''.join(path[::-1])
# 8. 探索四个方向
for dx, dy, direction in directions:
nx, ny = x + dx, y + dy
# 边界检查
if 0 <= nx < n and 0 <= ny < m:
# 可通行且未访问
if maze[nx][ny] == '0' and not visited[nx][ny]:
visited[nx][ny] = True
pre[nx][ny] = (x, y, direction)
queue.append((nx, ny))
return ""
为什么用队列,不用栈
python
队列(BFS):先进先出 → 一层一层搜索 → 找最短路径
栈(DFS):后进先出 → 一条路走到黑 → 找所有路径
visited 什么时候标记?
python
# ? 正确:入队时标记
if not visited[nx][ny]:
visited[nx][ny] = True # 立即标记
queue.append((nx, ny))
# ? 错误:出队时标记(会重复入队)
while queue:
x, y = queue.popleft()
visited[x][y] = True # 太晚了!
如何记录步数?
python
# 方法1:用distance数组
distance = [[0] * m for _ in range(n)]
distance[nx][ny] = distance[x][y] + 1
# 方法2:用层数变量
queue.append((x, y, step+1))
如何输出最短路径长度?
python
# 不需要记录具体路径时
if x == end_x and y == end_y:
return distance[x][y] # 返回步数
BFS模板的简化版(只求步数)
python
from collections import deque
def bfs():
queue = deque()
queue.append((0, 0))
visited = [[False] * m for _ in range(n)]
visited[0][0] = True
distance = [[0] * m for _ in range(n)]
while queue:
x, y = queue.popleft()
if x == n-1 and y == m-1:
return distance[x][y]
for dx, dy in [(1,0), (0,-1), (0,1), (-1,0)]:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m:
if maze[nx][ny] == '0' and not visited[nx][ny]:
visited[nx][ny] = True
distance[nx][ny] = distance[x][y] + 1
queue.append((nx, ny))
return -1