洛谷 官方题库 python 第七天 (DFS (深度优先搜索) 算法详解 + 模板 + 例题)

【算法1-7】搜索

预备知识:递归,记忆化

计算第 3000 个斐波那契数
import sys 
sys.setrecursionlimit(30000) #设置递归深度
def fib(n): 
 global cnt 
 cnt += 1 
 if n==1 or n==2: 
    data[n]=1; return data[n] 
 if data[n] != 0: 
    return data[n] ##记忆化判断:已经算过,不用再算,直接返回结果
 data[n] = fib(n-1)+fib(n-2) 
 return data[n] 
data=[0]*3005 
cnt = 0 
print(fib(3000)) #约为 4×10626
print(cnt) #递归次数,cnt=5997

import sys 
sys.setrecursionlimit(30000) #设置递归深度###不然默认1000 

DFS代码框架

# 定义全局变量表示答案
ans = None

# 定义深度优先搜索函数
def dfs(depth, other_parameters):
    # 使用全局变量 ans
    global ans
    
    # 判断是否到达最底层或者满足退出条件
    if 到达最底层或满足退出条件:
        # 更新答案
        更新答案
        return  # 返回到上一层
    
    # 剪枝操作,可以在进一步 DFS 之前剪枝
    
    # 枚举下一层可能的情况,并继续 DFS
    for 每一种情况 in 枚举下一层可能的情况:
        # 如果状态 i 没有被使用过,就可以进入下一层
        if  used[i]== 0:
            # 标记状态 i,表示已经使用过,在更深的层次不能再使用
            used[i] = 1
            
            # 进入下一层 DFS
            dfs(depth + 1, other_parameters)
            
            # 恢复状态,回溯时不影响上一层对这个状态的使用
            used[i] = 0
    
    return  # 返回到上一层
DFS 代码框架中
  used[i] = 1 ,称为“保存现场”或“占有现场”。
  used[i] = 0 ,称为“恢复现场”或“释放现场”
1 )“保存现场”的作用是禁止重复使用。当搜索一条从起点到终点的路径时,这条路径
上经过的点,不能重复经过,否则就兜圈子了,所以需要对路径上的点“保存现场”,禁止再
经过它。没有经过的点,或者碰壁后退回的点,都不能“保存现场”,这些点可能后面会进入
当前路径。
2 )“恢复现场”的作用是允许重复使用。当重新搜新的路径时,方法是从终点(或碰壁
的点)沿着旧路径逐步退回,每退回一个点,就对这个点“恢复现场”,允许新路径重新经过
这个点
continue 语句,以在特定条件下跳过当前循环迭代并继续执行下一次循环迭代
%s:用于格式化字符串。可以用来替换为任意类型的字符串,包括字符串、整数、浮点数等。
%d:用于格式化整数。用来替换为整数类型的值。
  • %f:用于格式化浮点数。
  • %r:用于格式化任意 Python 对象的字符串表示形式。
  • %x%X:用于格式化整数为十六进制表示形式。
搜索从起点到终点的所有路径:用dfs    而最短路用bfs
for i in range(wy): a[i]=list(input()) ##储存几行的输入
  
eg:
【题目描述】 给出一张图,输出从起点到终点的所有路径。
【输入描述】 输入的第一行是整数 n m ,分别是行数和列数。后面的 n 行,每行有 m
符号。“ @ ”表示起点,“ * ”表示终点,“ ”表示能走,“ # ”表示是墙壁不能走。每一步都按
下的顺序搜索。在样例中,左上角坐标为 (0, 0) ,起点坐标为 (1, 1) ,终点坐标为
(0, 2) 1< n , m <7
【输出描述】 输出所有的路径。坐标 ( i , j ) ij 表示,例如坐标 (0, 2) 表示为 02 。从左到右是
i ,从上到下是 j
【输入样例】
5 3
.#.
#@.
*..
...
#.#
【输出样例】
from 11 to 02
1: 11->21->22->12->02
2: 11->21->22->12->13->03->02
3: 11->21->22->23->13->03->02
4: 11->21->22->23->13->12->02
5: 11->12->02
6: 11->12->22->23->13->03->02
7: 11->12->13->03->02
def dfs(x, y):
    global num
    for i in range(4):
        dirs = [(-1, 0), (0, -1), (1, 0), (0, 1)] # 左、上、右、下
        nx, ny = x + dirs[i][0], y + dirs[i][1] # 新坐标
        if nx < 0 or nx >= hx or ny < 0 or ny >= wy:
            continue # 不在地图内
        if mp[nx][ny] == '*':
            num += 1
            print("%d: %s->%d%d" % (num, p[x][y], nx, ny)) # 输出路径

            ###p[x][y]???    这里的p[x][y]是很长一段

            continue # 不退出,继续找下一条路径
        if mp[nx][ny] == '.':
            mp[nx][ny] = '#' # 保存现场。这个点在这次更深的 DFS 中不能再用
            p[nx][ny] = p[x][y] + '->' + str(nx) + str(ny) # 记录路径
            dfs(nx, ny)
            mp[nx][ny] = '.' # 恢复现场。回溯之后,这个点可以再次用

num = 0
wy, hx = map(int, input().split()) # Wy 行,Hx 列。用 num 统计路径数量
a = ['']*10
for i in range(wy): 
    a[i] = input() # 读迷宫

##注意:如果直接把input转换为列表,x,y是错位的。eg:input:"." "#" "."
#a=[["." "#" "."],[],[],[],[]......],a[0][1]='#',此时0 对应 y ,1 对应 x
#所以 输入时还要把 x,y交换顺序,所有建立临时列表a,过渡到mp

mp = [[''] * 10 for _ in range(10)] # 二维矩阵 mp[][]表示迷宫
p = [[''] * 10 for _ in range(10)] # 记录从起点到点 path[i][j]的路径

for x in range(hx):
    for y in range(wy):
        mp[x][y] = a[y][x]
        if mp[x][y] == '@': 
            sx, sy = x, y # 起点
        if mp[x][y] == '*': 
            tx, ty = x, y # 终点

print("from %d%d to %d%d" % (sx, sy, tx, ty))
p[sx][sy] = str(sx) + str(sy)
dfs(sx, sy)

看懂后,进入实操

P1605 迷宫

def dfs(x,y):
    global cnm
    if mp[fy-1][fx-1]!="#":
        for i in range(4):
            dic=[(1,0),(-1,0),(0,1),(0,-1)]
            nx,ny=x+dic[i][0], y+dic[i][1]
            if nx<0 or ny<0 or nx>=N or ny>=M :
                continue
            if mp[nx][ny]=="*":
                cnm+=1
                continue

            if mp[nx][ny]==".":
                mp[nx][ny]="#"
                dfs(nx,ny)
                mp[nx][ny]="."

N,M,T=map(int,input().split())
sx,sy,fx,fy=map(int,input().split())
cnm=0
mp=[["."]*N for i in range(M)]

mp[sy-1][sx-1]="@"
mp[fy-1][fx-1]="*"
for i in range(T):
    px,py=map(int,input().split())
    mp[py-1][px-1]="#"
dfs(sy-1,sx-1)
print(cnm)
##很简单,如果你看懂了上面的例题

P1036 [NOIP2002 普及组] 选数

python 组合用,不用写dfs

for num in combinations(li, k):
    kk = sum(num)

P2404 自然数的拆分问题

n = int(input())
data = [0 for _ in range(110)]

def dfs(sum,p,cnt):
    if sum>n:
        return
    if sum==n:
        print(data[0],end="")
        for i in range(1,cnt):
            print("+%d"%data[i],end="")
        print()
    if sum<n:
        for i in range(p,n):
            data[cnt]=i
            dfs(sum+i,i,cnt+1)
dfs(0,1,0)

##感觉不是很懂这个return,的用法

P1219 [USACO1.5] 八皇后 Checker Challenge

  • 35
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值