【算法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,的用法