当前位置:   article > 正文

python实现dfs全套系列(思路+模版+优化+典型例题)_pythondfs

pythondfs

一.dfs初印象

 dfs是一种常用于图和树的遍历算法,用于遍历所有节点。要想掌握好dfs前提得理解递归的思想。dfs可以理解为一条路走到黑,不行,就返回,接着往下走。

二.dfs模板

基本思路分析:个人觉得重点是找到递归重复的部分,即找到题目中重复需要执行的部分,然后是递归出口的判断;再然后进行回溯,剪枝,记忆化搜索等优化。

  1. def dfs(depth):
  2. #depth为当层第几重循环即深度
  3. if depth==N:
  4. #当到达边界是,即返回
  5. return
  6. #每重循环进行枚举选择,即dfs递归处

三.优化

3.1 回溯

回溯就是在搜索尝试过程中寻找问题的解,当发现不满足条件时,就回溯,尝试别的路径。

强调的是此路不通,另寻他路。先打标记,记录路径;然后下一层,回到上一层,清除标记

3.1.1回溯法求全排列

  1. def dfs(depth):
  2. #depth表示当前层数,即已选的个数
  3. if depth==n:
  4. #输出每次的全排列
  5. print(path)
  6. return
  7. for i in range(1,n+1):
  8. #已标记
  9. if vis[i]:
  10. continue
  11. #未标记
  12. vis[i]=1
  13. path.append(i)
  14. #递归下一层
  15. dfs(depth+1)
  16. #回溯
  17. #修改标记
  18. vis[i]=0
  19. #将该数字弹出
  20. path.pop(-1)
  21. n=int(input())
  22. #标记数组,1为标记,0为未标记
  23. vis=[0]*(n+1)
  24. #路径数组
  25. path=[]
  26. dfs(0)

3.1.2回溯法解决n皇后问题

  1. import os
  2. import sys
  3. input=sys.stdin.readline
  4. ans=0
  5. #思路:遍历每一行,即遍历的深度,然后在每列中选择合适的位置
  6. def dfs(x):
  7. #出口
  8. if x==n+1:
  9. global ans
  10. ans+=1
  11. #第x层枚举每一列
  12. for y in range(1,n+1):
  13. #如果当前列,主对角线,副对角线已标记
  14. if vis1[y] or visa[x+y] or visb[x-y+n]:
  15. continue
  16. #如果没有标记,先标记,再递归下一行(此时是合法的点)
  17. vis1[y] =visa[x+y] = visb[x-y+n]=True
  18. #递归下一行
  19. dfs(x+1)
  20. #返回上一行,取消标记
  21. vis1[y] =visa[x+y] = visb[x-y+n]=False
  22. n=int(input())
  23. #标记数组
  24. #列
  25. vis1=[False]*(n+1)
  26. #主对角线
  27. visa=[False]*(2*n+1)
  28. #副对角线
  29. visb=[False]*(2*n+1)
  30. #从1开始
  31. dfs(1)
  32. print(ans)

3.2剪枝

主要可分为可行性剪枝和最优化剪枝。可行性剪枝:当前状态和题意不符,并且往后的所有情况和题意都不符,那么就可以剪枝。最优化剪枝:在搜索过程中,当前状态已经不如已经找到的最优解,也可剪枝,不需要继续搜索。

3.2.1剪枝解决数字王国之军训排队

1.数字王国之军训排队 - 蓝桥云课 (lanqiao.cn)


思路:

  • dfs搜索,枚举每个学生分到每个组内
  • 可行性剪枝:要满足题目条件
  • 最优性剪枝:判断当前状态是否比ans更劣

 

  1. import os
  2. import sys
  3. input=sys.stdin.readline
  4. #倍数关系判断
  5. #判断该学生是否放进当前分组
  6. def check(now_group,y):
  7. #遍历当前组中的元素
  8. for i in now_group:
  9. #是倍数关系
  10. if i%y==0 or y%i==0:
  11. return False
  12. return True
  13. def dfs(depth):
  14. #depth表示当前第几个学生
  15. #最优化剪枝(因为队数不能超过学生数)
  16. global ans
  17. if len(groups)>ans:
  18. return
  19. #递归出口
  20. if depth==n:
  21. ans=min(ans,len(groups))
  22. return
  23. #对于每个学生,枚举放在哪一组
  24. for now_group in groups:
  25. #now_group表示当前组
  26. #可行性剪枝
  27. if check(now_group,a[depth]):
  28. #放入
  29. now_group.append(a[depth])
  30. dfs(depth+1)
  31. now_group.pop()
  32. #直接另做一组
  33. groups.append([a[depth]])
  34. dfs(depth+1)
  35. groups.pop()
  36. n=int(input())
  37. a=list(map(int,input().split()))
  38. #分的队数,相当与一个二维数组
  39. groups=[]
  40. ans=n
  41. dfs(0)
  42. print(ans)

3.2.2 剪枝解决特殊的多边形 ​​​​​​​

 

 

 

  1. import os
  2. import sys
  3. input=sys.stdin.readline
  4. #dfs求所有的n边形,边长乘积不超过100000
  5. def dfs(depth,last_val,tot,mul):
  6. #depth 第depth边,last_val指上一条边长,tot即边长之和,mul即边长之积
  7. #递归出口
  8. if depth==n:
  9. #可行性剪枝
  10. #满足n边形基本定义
  11. #最小的n-1边之和大于第n边 tot-path[-1]>path[-1]
  12. if tot>2*path[-1]:
  13. #合法的n边形
  14. ans[mul]+=1
  15. return
  16. #枚举第depth条边的边长为i
  17. for i in range(last_val+1,100000):
  18. #最优化剪枝
  19. #先前选择了depth个数字,乘积为mul
  20. #后续还有n-depth个数字,每个数字都要>i
  21. if mul*(i**(n-depth))<=100000:
  22. path.append(i)
  23. dfs(depth+1,i,tot+i,mul*i)
  24. path.pop()
  25. else:
  26. break
  27. t,n=map(int,input().split())
  28. #ans[i]表示价值为i的n边形
  29. ans=[0]*100001
  30. path=[]
  31. dfs(0,0,0,1)
  32. #每次询问一个区间l,r,输出多少个n边形的价值在[l,r]中
  33. #等价于ans[l]+....+ans[r],需要对ans求前缀和
  34. for i in range(100001):
  35. ans[i]+=ans[i-1]
  36. for _ in range(t):
  37. l,r=map(int,input().split())
  38. print(ans[r]-ans[l-1])

3.3记忆化搜索

通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式

  1. #记忆化,直接加导这个包即可
  2. from functools import lru_cache
  3. #把普通化递归变成记忆化递归
  4. @lru_cache(maxsize=None)

3.3.1记忆化解决斐波那契数列

 

 

  1. #记忆化,直接加导这个包即可
  2. from functools import lru_cache
  3. #把普通化递归变成记忆化递归
  4. @lru_cache(maxsize=None)
  5. def f(x):
  6. if x==0 or x==1:
  7. return 1
  8. return f(x-1)+f(x-2)

 3.3.2记忆化解决混沌之地

用户登录

 ​​​​​​​

 

  1. import sys
  2. input=sys.stdin.readline
  3. from functools import lru_cache
  4. @lru_cache(maxsize=None)
  5. def dfs(x,y,z):
  6. #x当前横坐标,y当前纵坐标,z有无使用背包
  7. #出口
  8. if x==C and y==D:
  9. return True
  10. #未抵达出口,往四个方向走
  11. for dx,dy in [(0,1),(0,-1),(1,0),(-1,0)]:
  12. xx,yy=dx+x,dy+y
  13. #边界判断
  14. if xx<0 or xx>=n or yy<0 or yy>=m:
  15. continue
  16. #新位置高度低于当前位置
  17. if a[xx][yy]<a[x][y]:
  18. #直接走
  19. if dfs(xx,yy,z):
  20. return True
  21. #新位置高度高于当前位置,且相差小于k,用掉背包
  22. if a[xx][yy]-a[x][y]<k and z==False:
  23. if dfs(xx,yy,True):
  24. return True
  25. return False
  26. n,m,k=map(int,input().split())
  27. A,B,C,D=map(int,input().split())
  28. A,B,C,D=A-1,B-1,C-1,D-1
  29. a=[]
  30. for i in range(n):
  31. a.append(list(map(int,input().split())))
  32. if dfs(A,B,False):
  33. print("Yes")
  34. else:
  35. print("No")

 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号