蓝桥杯第7天(Python组)

文章展示了DFS在实现排列、全排列、解决迷宫问题以及进行剪枝搜索等场景的应用。通过不同的DFS实现方式,如主流DFS和二进制输出,以及在不同问题中的变体,如寻找路径和处理连通分量,阐述了DFS的灵活性和效率。同时,还提到了剪枝策略在优化搜索过程中的重要性。
摘要由CSDN通过智能技术生成
两种DFS的实现
# def dfs(s, t):  # 区间范围[s:t]
#     if s == t:  # 终止条件
#         print(a[0:n])
#     else:
#         for i in range(s, t + 1):  # 交换首元素,从s到t交换一次
#             a[s], a[i] = a[i], a[s]
#             dfs(s + 1, t)
#             a[i], a[s] = a[s], a[i]
#
#
# a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# n = 3
# dfs(0, n - 1)

# 主流DFS写法
# def dfs(s, t):  # 区间范围[s:t]
#     if s == t:  # 终止条件
#         print(b[0:n])
#     else:
#         for i in range(t):  # 从0-n开始作为首元素
#             if vis[i]== False:  # 检查没被用过的
#                 vis[i]=True
#                 b[s]=a[i]  # 存排列
#                 dfs(s + 1, t)
#                 vis[i]=False
#
#
# a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# b=[0]*10  # 记录全排列
# vis = [False]*10  # 检查是否用过
# n = 3
# dfs(0, n)   #前n个数的全排列

DFS实现排列

# DFS 实现排列,二进制输出
# vis = [0]*10
# def dfs(k):  # 深搜到第k个
#     if k==4:
#         b=map(str,vis[:4])
#         print(''.join(b))
#
#         # for i in range(4):
#         #     print(vis[i],end=' ')
#         # print()
#     else:
#         vis[k]=0  # 不选第k个
#         dfs(k+1)
#         vis[k]=1  # 选第k个
#         dfs(k+1)
# dfs(0)



def dfs(k):  # 深搜到第k个,以10进制输出
    if k==3:
        for i in range(3):
            if vis[i]==1:
                print(a[i],end=' ')
        print()
    else:
        vis[k]=0  # 不选第k个
        dfs(k+1)
        vis[k]=1  # 选第k个
        dfs(k+1)
vis = [0]*10
a=[1,2,3,4,5,6,7,8,9,10]
dfs(0)

迷宫
# bfs搜索,顺便记录路径
# dfs反向搜索记录路径并输出
def dfs(x, y):  # 从(x,y)处开始搜索
  if (x==29 and y==49): # 到达终点
    return 1
  if vis[x][y]==1 : # 走过该点,没走出去,说明出不去
    return 0
  vis[x][y]=1
  if mp[x][y] =='L':
    return dfs(x-1,y)
  if mp[x][y] =='R':
    return dfs(x+1,y)
  if mp[x][y] =='U':
    return dfs(x,y+1)
  if mp[x][y] =='D':
    return dfs(x,y-1)


track=[]
# 30 行 50 列
mp = [[''*50]for i in range(30)]  # 存储迷宫,预先分配大小,不然赋值会报错
for i in range(10):  # 存储迷宫
  mp[i] = list(input())

vis = [[0]*50 for i in range(30)]
dfs(0,0)

# 记录路径没有写!!
寒假作业题(排列+DFS)
def dfs(num):  # (1-13)
  global ans
  if(num ==13): #终止条件
    if b[10]==b[11]*b[12]:  # 将除法转为乘法更快速
      ans+=1
    return
  # 剪枝处理
  if num ==4 and b[1]+b[2]!=b[3] : return
  if num ==7 and b[4]-b[5]!=b[6] : return
  if num ==10 and b[7]*b[8]!=b[9] : return

  for i in range(1,14): # 按字典序遍历
    if not vis[i]: 
      b[num]=i
      vis[i]=1  # 保护现场
      dfs(num+1) # 相当于固定了前面几个,遍历后面几个
      vis[i]=0 #恢复现场

ans =0
b=[0]*15  # 记录排列
vis=[0]*15  # 记录是否遍历
dfs(1)  # (1-13)
print(ans)

找被淹没岛屿,DFS+连通分量
import sys
sys.setrecursionlimit(60000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题

def dfs(x,y):
  d=[(-1,0),(0,1),(1,0),(0,-1)]  # 左 上 右 下
  global flag
  global vis  # 判断是否走过
  global mp  # 地图
  
  vis[x][y] =1
  if mp[x][y+1]=='#' and mp[x][y-1] =='#' and mp[x+1][y]=='#' and mp[x-1][y]=='#':
    # 说明点(x,y)四周都是陆地
    flag = 1  # 高地标记,不会被淹没
  for i in range(4):
    nx=x+d[i][0]
    ny=y+d[i][1]
    if vis[nx][ny] ==0 and mp[nx][ny] =='#':
      # 如果当前没有遍历点(nx,ny)同时地图上面该点不是陆地
      dfs(nx,ny)

n=int(input())
mp = []   # 记录地图
for i in range(n):
  mp.append(list(input()))
vis =[]   # 判断是否走过
for i in range(n):
  vis.append([0]*n)

ans =0
for i in range(n):  # 遍历每一点
  for j in range(n):
    if vis[i][j] ==0 and mp[i][j] =='#':  # 相当于找连通分量
      flag = 0
      dfs(i,j)
      if flag==0:
        ans+=1

print(ans)
    

DFS问题

import sys
sys.setrecursionlimit(300000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题

def dfs(u,fa):
  # 计算当前结点 左子, 右子,和父结点的连通度
  global num ,maxnum   # 教父数量
  d[u]=1  # 最少存在一个,即叶子结点
  temp =0
  for v in edges[u]:
    if v == fa:  continue   # 父结点不再遍历
    dfs(v,u)   # 递归叶子节点
    d[u]=d[u]+d[v]  # 计算当前结点数量
    temp =max(temp,d[v])  # 计算子节点当前最大连通块数量
  temp = max(temp,n-d[u])  # 计算当前结点最大连通块数量

  if temp<maxnum:  # 更新教父
    maxnum = temp
    num =1
    ans[1]=u
  elif temp ==maxnum:  # 发现疑似教父
    num+=1
    ans[num]=u
    
 
maxnum = int(1e9)
n = int(input())
d = [0]*(n+1)  # 记录以u为子树的结点数量
ans = [0] * (n+1)  # 记录教父
num = 0  # 记录教父数量

edges =[[]for i in range(n+1)]  # 存储邻接边
for i in range(n-1):   # 以邻接边形式存储树
  a,b = map(int,input().split())
  edges[a].append(b)
  edges[b].append(a)

dfs(1,0)
s=sorted(ans[1:num+1])
for i in range(num):  # 根据教父数量打印教父,当有相同最小值时会出现多个教父
  print(s[i],end=' ') 
DFS搜索问题

import sys
sys.setrecursionlimit(300000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题


def dfs(x,y,c,s): # (X,Y,count,sum)
  global sum_num,ans
  if 2*s > sum_num :  # 剪枝
    return 
  if 2*s ==sum_num:  # 找到合适的方格
    if ans>c and vis[0][0]==1:  # 找到了更优解更新
      ans =c
  vis[x][y]=1 # 保存现场
  dir = [(-1,0),(0,1),(1,0),(0,-1)] # 左上右下
  for u ,v in dir:  #往四个方向遍历
    nx,ny = x+u,y+v
    if(nx>=0 and ny>=0 and nx<n and ny <n):  # x,y 取值都是[0,n-1]
      if vis[nx][ny]==0:    #没有遍历过
        dfs(nx,ny,c+1,s+a[x][y])
  vis[x][y]=0   #恢复现场
    
m,n = map(int,input().split())  # 读入输入大小,n*m
a = [list(map(int,input().split())) for _ in range(n)]  #读入数据
vis = [[0]*m for _ in range(n)]  # 判断是否走过

sum_num = 0
for i in a:  # 计算总和
  sum_num += sum(i)
  

ans = 1000
dfs(0,0,0,0)
print(ans)

找路径问题
import sys
sys.setrecursionlimit(300000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题

def dfs(x,y):
  if a[x]<0 or b[y]<0: return  # 直接剪枝
  if x==n-1 and y==n-1: # 到达终点
    if sum(a)!=0 or sum(b)!=0 :  # 说明不是正确顺序
       return 
    for i in range(len(path)):
        print(path[i],end=' ')

  # 遍历
  d = [(1,0),(-1,0),(0,1),(0,-1)]
  for u,v in d:
    nx=x+u;ny=y+v
    if 0<=nx<n and 0<=ny<n and vis[nx][ny]==0:  # 判断坐标是否合法,并且没有被访问
      vis[nx][ny]=1  #保护现场
      path.append(nx*n+ny)   # 进栈,保存路径
      a[nx]-=1 
      b[ny]-=1
      dfs(nx,ny)
      path.pop()    # 出栈,那条路走不通
      a[nx]+=1
      b[ny]+=1
      vis[nx][ny]=0   # 恢复现场,之前那条路走不通,换路



n = int(input())
path = []  # 创建一个栈用于保存路径
# 保存射箭数
b = list(map(int,input().split()))  # 西——> 东 x方向
a = list(map(int,input().split()))  # 北——> 南 y方向
vis = [[0]*n for _ in range(n)]  # 用于判断是否到达
# 从(0,0) 开始搜索
vis[0][0]=1
a[0]-=1
b[0]-=1
path.append(0)
dfs(0,0)
剪枝搜索
import sys
sys.setrecursionlimit(300000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题

def dfs(n):
  global cnt
  if n>4 and m[1]+m[2]+m[3]+m[4] !=34 :return  # 剪枝
  if n>8 and m[5]+m[6]+m[7]+m[8] !=34 :return  # 剪枝
  if n>12 and m[9]+m[10]+m[11]+m[12] !=34 :return  # 剪枝
  if n>13  and m[4]+m[7]+m[10]+m[13] !=34 :return  # 剪枝
  if n>14 and m[2]+m[6]+m[10]+m[14] !=34 :return  # 剪枝
  if n>15  and m[3]+m[7]+m[11]+m[15] !=34 :return  # 剪枝
  if n>16 and m[4]+m[8]+m[12]+m[16] !=34 \
     or m[13]+m[14]+m[15]+m[16] !=34\
     or m[1]+m[6]+m[11]+m[16] !=34  :return  # 剪枝
  if n==17:
    cnt+=1
  for i in range(2,17):
    if(vis[i]==0):
      m[n]=i    #给m赋值
      vis[i]=1
      dfs(n+1)
      vis[i]=0

cnt = 0 # 有多少种方案
m = [0]*17
m[1]=1  # 左上角固定为1
vis = [0]*17  # 看是否使用
vis[1]=1
dfs(1)
print(cnt)

# 1 -- 16

感悟(要保存现场,恢复现场,读入矩阵,一个到达矩阵(与读入矩阵相同),左上右下,遍历,可以通过栈来记录路径,同时剪枝优化)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值