深度优先搜索

​ 本节我们要讲解的算法是深度优先搜索(DFS)。顾名思义,广度优先搜索和深度优先搜索的区别在于,深度优先搜索是一条路走到底,再继续寻找下一条路;而广度优先搜索则是试图探寻每一步可能的路径。

按照惯例,我们先来看一道板子题:

  • 题目背景

    给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

  • 题目描述

    无。

  • 输入格式

    第一行N、M和T,N为行,M为列,T为障碍总数。

    第二行起点坐标SX,SY,终点坐标FX,FY。

    接下来T行,每行为障碍点的坐标。

  • 输出格式

    给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

  • 解析

    • 递归

      众所周知,递归就是函数自身调用自身的过程。

      如果我们定义一个叫做DFS的函数,函数参数中包含坐标,然后在函数中判断下一次可走的位置,并且再次调用DFS函数,这就是递归。

      而这种递归一般都是深度优先搜索,因为这是一条路径走到头(递归到递归终止条件为止)。

    • 回溯

      有的时候,我们需要用到回溯。

      比如说迷宫,我们要求每个方块只能走一次,那么我们就必须在走过一个路径之后,标记它为不可走。

      但是当本次递归结束,我们又需要把这个路径标记回可走,这就是回溯。

      简单地说,回溯就是回到上一次运行的状态。

  • 思路

    思路大概就是,先创建一个地图,自己填障碍,然后从起点开始调用DFS函数。每当DFS函数遇到终止条件时记一次数。

  • 代码
m,n,t = map(int,input().split())#输入m,n,t
ls=[[0 for j in range(m)] for i in range(n)]#创捷一个m行n列的空列表
dir = [[0,-1],[0,1],[1,0],[-1,0]]#方向数组四个方向
ans = 0#答案初始化为0
f_x,f_y,e_x,e_y = map(int,input().split())#输入初始坐标和结束坐标
for i in range(0,t):#障碍输入
    x,y = map(int,input().split())
    ls[x-1][y-1] = 1
    
def DFS(x,y):#DFS函数核心
    if x>m-1 or y>n-1 or x<0 or y<0:#如果越界,返回
        return
    if ls[x][y] == 1:#如果为1,返回
        return
    if x == e_x - 1 and y == e_y - 1:#如果到终点,计数并返回
        global ans
        ans += 1
        return
    for i in range(0,4):#如果执行到这里,说明没有返回。那么这个点可以走,向它的四个方向继续遍历
        ls[x][y] = 1#标记
        DFS(x+dir[i][0],y+dir[i][1])#下一次
        ls[x][y] = 0#回溯

DFS(f_x-1,f_y-1)#调用

print(ans)#输出答案

下面是习题时间~

八皇后

  • 题目描述

    一个如下的 6 ×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

    img

    上面的布局可以用序列 2 4 6 1 3 5来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:
    行号 1 2 3 4 5 6
    列号 2 4 6 1 3 5
    这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
    并把它们以上面的序列方法输出,解按字典顺序排列。
    请输出前3个解。最后一行是解的总个数。

  • 输入格式

    一行一个正整数 n,表示棋盘是 n×n 大小的。

  • 输出格式

    前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

  • 思路

    BFS函数按行递归,使用两个列表低复杂度的标记每一个结点所在行和斜行,统计到达底部且可行的次数。

  • 代码
n = int(input())
ans = 0
mark_y = []
mark_xie = [0 for i in range(n * 10)]
mark_xie_other = [0 for i in range(n * 10)]

def DFS(m):
    for i in range(n):
        Judge = True
        if i in mark_y:#如果标记了列,不行
            Judge = False
        if mark_xie[n-i+m]:#如果标记了斜,不行
            Judge = False
        if mark_xie_other[m+i]:
            Judge = False
        if i == n-1 and not Judge: #如果到n-1了(本行全都走完了),还走不了,就得返回上一层了
            return

        if Judge:#如果可以标记
            if m == n-1:#而且进行到最后一行了
                global ans
                ans += 1#那ans就可以++了。
            else:#如果没到最后一行
                mark_y.append(i)#标记列
                mark_xie[n-i+m] = 1#标记斜
                mark_xie_other[m+i] = 1#标记斜

                DFS(m+1)#下一层搜索

                #每当一个return运行结束后 进入此处
                #如果到这里,说明下一行全不行了,需要返回上一行进行下一次循环
                #所以列表回溯
                mark_y.remove(i)#
                mark_xie[n-i+m] = 0
                mark_xie_other[i+m] = 0

DFS(0)
print(ans)

本文转载自本人的个人博客,www.fengjx.cn。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值