关于深搜DFS

      关于深搜

       深搜:深度优先搜索(DFS)Depth First Search,是一种用于遍历搜索树或图的算法,沿着树的深度遍历树的节点,尽可能深的搜索树的分支。

  深搜坚持  ”不撞南墙不回头”理念

       首先选取一个未访问的点作为源点,从源点选取一条路一直往下走,直到走不下去为止,这时返回上一顶点继续试探访问此顶点的其余子顶点直到当前顶点的子顶点都被访问过,那么返回上一顶点,继续重复,从而实现遍历。

          

如图所示我们选取A节点作为源点,向下一层进行遍历访问到节点B, 节点B继续向下一层遍历访问到节点C, 节点C继续向下一层访问到节点E, 节点E之后再无没有遍历过的子节点,那么返回上一层节点C,节点C之后也无没有遍历过的子节点,那么继续返回上一节点B,节点B的之后有未被访问的节点D,那么遍历访问节点D,节点D之后没有被访问的子节点,返回上一节点B,节点B之后没有未被访问的子节点那么返回节点A,节点A之后没有未被访问的子节点,至此整个深搜过程DFS结束

      故此图的遍历搜索顺序为:A->B->C->E->D

 以上可以看出深搜的大概思路就是沿着某一条路径进行遍历,走到尽头的时候,再遍历另一条路径(分岔路),重复遍历过程,直到所有节点都被访问,即回溯到源节点并且源节点已无未被访问的子节点。

DFS的精髓在于递归求解的思路以及回溯的处理,针对搜索的过程,又有重要的剪枝优化,必要的剪枝优化对DFS的顺序执行有很大的作用

     什么是回溯?回溯算法是能够在数的结构中搜索到通往特定终点的一条或多条特定路径,回溯的算法思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试,从而搜索到抵达终点的一条或多条特定路径,值得注意的是回溯法以深度优先搜索的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索

            回溯算法=深度优先搜索+剪枝函数

DFS的应用模板

void  dfs(int  step)//当前状态
{
 //判断边界条件
 if(xxx){
  return;//返回上一步
 }
 //尝试每一种可能
 for(xxx){
  //判断是否符合条件
  if(xxx){
   //继续走下一步
   dfs(step+1);
  }
 }
 return;//全部遍历做完后返回
}

下面结合DFS的经典案例来探讨DFS的应用

1.洛谷p1706全排列问题

#include<bits/stdc++.h>
using namespace std;
 
int a[11],b[11];
int n;
 
void dfs(int step)
{
    if(step==n+1){
        for(int i=1;i<=n;i++)
            cout<<"    "<<a[i];
        cout<<endl;
        return;
    }
    for(int i=1;i<=n;i++){
        if(b[i]==0){      //判断数是否用过
            a[step]=i;    //使用该没用过的数
            b[i]=1;       //用过的数标记
            dfs(step+1);  //向下遍历
            b[i]=0;       //回溯恢复成原来没用过的的状态 (恢复现场)
        }
    }
    return;
}
 
 
int main()
{
    while(cin>>n){
        dfs(1);    
    }
}

输入样例:

3

输出样例:

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

套用DFS的过程如下

洛谷p1219八皇后

题目描述

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

上面的布局可以用序列 2 4 6 1 3 52 4 6 1 3 5 来描述,第 �i 个数字表示在第 �i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 61 2 3 4 5 6

列号 2 4 6 1 3 52 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。

输入格式

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

输出格式

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

 输入案例

6

输出案例

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

(一) 首先我们要找规律 怎样来判定 行,列,对角线上的点已经被占用

 1)同列的点j的值是一样的

 2)同对角线,i+j的值是相同的

 3)同顺对角线,i-j的值是相同的

 4)对于行排列每次只会选取一个点,所以对行不需要约定

  (二)  如何设置状态位?

#include<bits/stdc++.h>
using  namespace std;
cinst int N=15;
int book[N],N,cnt;
bool st[N];
bool dg[N*2],udg[N*2];
void dfs(int x)
{
    if(x==n+1)
    {
        for(int i=1;i<=n;i++){
            cout<<book[i]]<<" ";
            cout<<endl;
            return ;
    }
    for(int y=1;y<=n;y++)      //对于一行中的每一个数都需要枚举
    {
        if(st[y]||dg[x+y]||udg[n+x-y]) continue; //枚举判断是否被占用
        book[x]=y;   //
        st[y]=1;
        dg[x+y]=1;
        udg[n+x-y]=1;
 dfs(x+1);
        st[y]=0;        //回溯
        dg[x+y]=0;      //回溯
        udg[n+x-y]=0;   //回溯
        book[x]=0;      //回溯
       
       
    }
}

int main()
{
    cin>>n;
    dfs(1);
    cout<<cnt<<endl;
    return 0;
}

洛谷p1605迷宫

题目描述

给定一个 N×M 方格的迷宫,迷宫里有 T 处障碍,障碍处不可通过。

在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。

输入格式

第一行为三个正整数 N,M,T,分别表示迷宫的长宽和障碍总数。

第二行为四个正整数 SX,SY,FX,FY,SX,SY 代表起点坐标,FX,FY 代表终点坐标。

接下来 T 行,每行两个正整数,表示障碍点的坐标。

输出格式

输出从起点坐标到终点坐标的方案总数。

输入输出样例

2 2 1
1 1 2 2
1 2

输出

1
#include<bits/stdc++.h>
using namespace std;
int n,m,t,sx,sy,fx,fy,cnt;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
bool st[9][9];
void dfs(int cx,int cy){
    if(cx==fx&&cy==fy)    //到达终点
    {
        cnt++;
        return;
    }
    for(int i=0;i<4;i++)
    {
        int nx=cx+dx[i];
        int ny=cy+dy[i];    //dx和dy表示上下左右移动后坐标的变化
        if(nx<1||nx>n||ny<1||ny>m||st[nx][ny])
        continue;
        st[nx][ny]=true;
        dfs(nx,ny);
st[nx][ny]=false;       //回溯
        
    }
}
int main()
{
    cin>>n>>m>>t;
    cin>>sx>>sy>>fx>>fy;
    while(t--)
    {
        int x,y;
        cin>>x>>y;
        st[x][y]=true;
    }
    st[sx][sy]=true;
    dfs(sx,sy);
    cout<<cnt<<endl;
    return 0;
}

   第一次发博客 ,知识水平不高,如有错误还请大佬多多指正!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值