深度优先搜索--DFS

算法介绍

作为搜索算法的一种,DFS对于寻找一个解的NP(包括NPC)问题作用很大。但是,搜索算法毕竟是时间复杂度是O(n!)的阶乘级算法,它的效率非常低,在数据规模变大时,这种算法就显得力不从心了。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
总结来说就是,这个算法运用的时候就是找一个头结点,然后沿着这个头结点一直找下去,直到走到最后一个满足条件的分节点,然后再寻找另一条路径,当沿着一条路走不满足条件时会自动的跳入上一层节点进行判断。dfs算法通常与回溯算法一起使用。

代码框架

void dfs(主要控制参数)
{
    
    
   if(参数达到临界条件)
     return; //终止
   if(判定当前方案合法或还没有被标记)
   {
    
    
      标记当前位置;
      dfs(下一位置);
      还原位置;//回溯
   }
}

案例

1、输出1~n的全排列

#include <iostream>
using namespace std;
int a[100]={
    
    0}; //用于存放待存入的数
int vis[100]={
    
    0};
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(vis[i]==0) //判断i是否已经被填到数组a中了
        {
    
    
          a[step]=i;
          vis[i]=1;   //标记i被填入
          dfs(step+1);  //填写下一个
          vis[i]=0;     //回溯,寻找其他解

        }
    }
}
int main()
{
    
    
   cin >> n;
   dfs(1);
    return 0;
}

2、迷宫问题<1>

题目:某人掉进了一个迷宫里面,请你告诉他是否有机会逃出去,第一上输入两个整数n和m表示一个n×m的迷宫。接下来的输入一个n行m列的迷宫。其中‘S’表示该人的起始位置,‘*’表示墙,无法通过,’.'表示路,可以通过,‘T’表示迷宫的出口,如果可以逃出去输出yes,否则输出no。
1<=m,n<=10;
输入格式:

3 4
S**.

***T

输出格式:

yes

#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int flag=0;
int dir[4][2]={
    
    1,0,-1,0,0,1,0,-1};
int m,n;
int q,p;
void dfs(int x,int y)
{
    
    
    if(a[x][y]=='T')
    {
    
    
            flag=1;
            return;
    }
    for(int i=0;i<=3;i++)
    {
    
    
        int tx=x+dir[i][0];
        int ty=y+dir[i][1];
        if(tx>=1&&tx<=m&&ty>=1&&ty<=n&&a[tx][ty]!='*'&&vis[tx][ty]==0)
        {
    
    
            vis[tx][ty]=1;
            dfs(tx,ty);
            vis[tx][ty]=0;
        }
    }
}
int main()
{
    
    
       while(cin >> m >> n)
       {
    
    
       memset(vis,0,sizeof(vis));
       flag=0;
       for(int i=1;i<=m;i++)
       {
    
    
          for(int j=1;j<=n;j++)
         {
    
    
            cin >> a[i][j];
            if(a[i][j]=='S')
            {
    
    
                p=i;
                q=j;
            }
         }
       }
    dfs(p,q);
    if(flag==1)
       {
    
    
           cout << "yes" << endl;
       }
    else if(flag==0)
       {
    
    
            cout << "no" << endl;
       }
       }
    return 0;
}

3、迷宫问题<2>进阶

题目:某人掉进了一个迷宫里面,请你告诉他是否有机会逃出去,第一上输入两个整数n和m表示一个n×m的迷宫。接下来的输入一个n行m列的迷宫。其中‘S’表示该人的起始位置,‘*’表示墙,无法通过,’.'表示路,可以通过,‘T’表示迷宫的出口,如果可以逃出去输出逃出去的最少步数step,若不能输出-1

#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int flag=0;
int dir[4][2]={
    
    1,0,-1,0,0,1,0,-1};
int m,n;
int q,p;
int ans=9999999999;
void dfs(int x,int y,int step)
{
    
    
    if(step>ans)
    {
    
    
        return;
    }
    if(a[x][y]=='T')
    {
    
    
            flag=1;
            if(ans>step)
            {
    
    
                ans=step;
            }
            return;
    }
    for(int i=0;i<=3;i++)
    {
    
    
        int tx=x+dir[i][0];
        int ty=y+dir[i][1];
        if(tx>=1&&tx<=m&&ty>=1&&ty<=n&&a[tx][ty]!='*'&&vis[tx][ty]==0)
        {
    
    
            vis[tx][ty]=1;
            dfs(tx,ty,step+1);
            vis[tx][ty]=0;
        }
    }
}
int main()
{
    
    
     cin >> m >> n;
       for(int i=1;i<=m;i++)
       {
    
    
          for(int j=1;j<=n;j++)
         {
    
    
            cin >> a[i][j];
            if(a[i][j]=='S')
            {
    
    
                p=i;
                q=j;
            }
         }
       }
    dfs(p,q,0);
    if(flag==1)
       {
    
    
           cout << ans<< endl;
       }
    else if(flag==0)
       {
    
    
            cout << -1 << endl;
       }
    return 0;
}

4、红与黑----变化

题目:蒜厂有一间长方形的房子,地面上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。
请写一个程序,计算你总共能够到达多少块黑瓷砖。
输入格式:第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都超不过20。
在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下

1)’.’:黑色的瓷砖
2)’#‘:白色的瓷砖
3)’@‘:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合 中唯一出现一次。

输出格式:输出一行,显示从起始位置出发能到达的瓷砖数(计数时包括起始位置)

#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int dir[4][2]={
    
    1,0,-1,0,0,1,0,-1};
int w,h;
int q,p;
int ans;
void dfs(int x,int y)
{
    
    
    for(int i=0;i<=3;i++)
    {
    
    
        int tx=x+dir[i][0];
        int ty=y+dir[i][1];
        if(tx>=1&&tx<=h&&ty>=1&&ty<=w&&a[tx][ty]=='.'&&vis[tx][ty]==0)
        {
    
    
            ans++;
            vis[tx][ty]=1;
            dfs(tx,ty);
        }
    }
}
int main()
{
    
    
     while(cin >>w>>h)
      {
    
    
           ans=1;
      memset(vis,0,sizeof(vis));
       for(int i=1;i<=h;i++)
       {
    
    
          for(int j=1;j<=w;j++)
         {
    
    
            cin >> a[i][j];
            if(a[i][j]=='@')
            {
    
    
                p=i;
                q=j;
            }
         }
       }
    dfs(p,q);
   cout << ans << endl;
      }
    return 0;
}

5、求最短路径

假设 一张地图 N行M列 ,0表示通畅1表示障碍物,P,Q表示终点坐标.求点(0,0)到(P,Q)最短路径

输入:
p q
m n
地图
输出:
路径
最短步数

#include <bits/stdc++.h>
using namespace std;
int ans=99999999999; 
int a[50][50]={
    
    0};    //地图 
int temp[50][50]={
    
    0}; //判断是否已经走过了 
int p,q;              //终止位置 
int m,n;//大小 
int dir[4][2]={
    
    {
    
    0,1},{
    
    0,-1},{
    
    1,0},{
    
    -1,0}};  //向哪个方向走 
void dfs(int x,int y,int step)
{
    
    
	if(x==p-1&&y==q-1)
	{
    
    
		if(step<=ans)
		{
    
    
			ans=step;  //如果路径更小则更换 
		}
		return;
	 }
	 for(int i=0;i<4;i++)
	 {
    
    
	 	int tx=x+dir[i][0];
	 	int ty=y+dir[i][1];
	 	if(tx>=0&&tx<=m-1&&ty>=0&&ty<=n-1&&a[tx][ty]==0&&temp[tx][ty]==0) //约束条件:保证在地图里边,保证该方向能走,保证没有来过这个地方 
	 	{
    
    
	 		temp[tx][ty]=1;
	 		cout << dir[i][0] << dir[i][1] << step+1 << endl; //输出所走的路径 
			dfs(tx,ty,step+1);
			temp[tx][ty]=0;	
		}
	 }
}
int main()
{
    
    
	cin >> p >> q;
	cin >> m >> n;
	for(int i=0;i<m;i++)
	{
    
    
		for(int j=0;j<n;j++)
		{
    
    
			cin >> a[i][j];
		}
	}
	dfs(0,0,0);
	cout << ans << endl;
	return 0;
}

6、八皇后问题

详情
该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

#include <bits/stdc++.h>
using namespace std;
int place[8]={
    
    0};
int d1[15];
int d2[15];
memset(d1,1,sizeof(d1));
memset(d2,1,sizeof(d2));
int flag[8];
memset(flag,1,sizeof(flag));
int nums=0;
void dfs(int n)
{
    
    
	if(n==8)
	{
    
    
		nums++;
	}
	int col;
    for(col=0;col<8;col++)
    {
    
    
        if(flag[col]&&d1[n-col+7]&&d2[n+col]) //判断该位置是否可以放置皇后
        {
    
    
            place[n]=col;
            flag[col]=false;
            d1[n-col+7]=false;
            d2[n+col]=false;
            dfs(index+1);
            //回溯,寻找其他可行组
            flag[col]=true;
            d1[n-col+7]=true;
            d2[n+col]=true;

        }
    }
}
int main()
{
    
    
	dfs(0);
	cout << nums << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45837693/article/details/108072172