DFS--深度优先搜索

来源:互联网 发布:java中变量的定义 编辑:程序博客网 时间:2024/06/15 08:16

DFS

     --深度优先搜索

1 树(二叉树)的三种遍历

先序:根-左-右中序:左-根-右后序:左-右-根

伪代码:
先序

     void PreoderTraversal(BinTree BT){                if(BT) {                    print(BT->data);                    PreoderTraversal(BT->left);                    PreoderTraversal(BT->right);                    }  } ```

中序

void InorderTraversal(Bin BT){     if(BT){             InorderTraversal(BT->left);             print(BT->data);             InorderTraversal(BT->right);           }     }

后序

  void PostTraversal(Bin BT)  {      if(BT)     {     PostTraversal(BT->left);     PostTraversal(BT->right);     print(BT->data);     } }

深度优先搜索的特点:

为了实现深度优先搜索,首先选择一个起始顶点并需要遵守三个规则:
(1) 如果可能,访问一个邻接的未访问顶点,标记它,并把它放入栈中。
(2) 当不能执行规则1时,如果栈不空,就从栈中弹出一个顶点。
(3) 如果不能执行规则1和规则2,就完成了整个搜索过程。

int dfs(int x,int y){        stack< node > s;     node a=1;     s.push(a);     vis[1]=1;     while(!s.empty())     {              node  tmp=s.top();           int flag=0;           for(int i=n-1; i>=0; i--)               {                    if(!vis[i] &&map[tmp][i])                    {                         s.push(i);                         vis[i]=1;                         flag=1;                     }                }           if(!flag)                 s.pop();   }

例题

类型一

1 Lake Counting
2 Oil Deposits
3 Red and Black

这三个例题的解法都差不多,利用了dfs深度优先搜索。针对连续块,计算块的个数(1和2),或者计算能够走到的最多步数(3),也是类似计算块的个数。其中,运动的方式可以用数组来表示。

此处给出2和3的代码:

题2

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.
Input
The input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either *', representing the absence of oil, or@’, representing an oil pocket.
Output
For each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets.
Sample Input
1 1
*
3 5
@@*
@
@@*
1 8
@@**@*
5 5
**@
@@@
@*@
@@@*@
@@**@
0 0
Sample Output
0
1
2
2

#include<iostream>#include<cstdio>#include<cstring>using namespace std;char a[102][102];int dfs(int i,int j);int main(){    int n,m,i,j;   while(scanf("%d%d",&n,&m)!=EOF)   {       if(m==0)        return 0;       else       {         int num=0;         memset(a,'*',sizeof(a));//先初始化         for(i=1; i<=n; i++)         for(j=1; j<=m; j++)            cin>>a[i][j];//油田的分部情况         for(i=1; i<=n; i++)         {            for(j=1; j<=m; j++)           {              if(dfs(i,j))              num++;//油田计数           }         }        printf("%d\n",num);      }  }}int dfs(int i,int j){   if(a[i][j]=='@')   {     a[i][j]='*';//相当于vis[i][j]=1标记走过,便不会再重复走。     dfs(i-1,j-1); dfs(i-1,j); dfs(i-1,j+1);      dfs(i,j-1);                dfs(i,j+1);     dfs(i+1,j-1); dfs(i+1,j); dfs(i+1,j+1);//周围的油田都走遍     return 1;   }   else    return 0;//但中间不能走时,就形成了一个油田}

题3

There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can’t move on red tiles, he can move only on black tiles.

Write a program to count the number of black tiles which he can reach by repeating the moves described above.
Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.

There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.

‘.’ - a black tile
‘#’ - a red tile
‘@’ - a man on a black tile(appears exactly once in a data set)
The end of the input is indicated by a line consisting of two zeros.
Output
For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).
Sample Input

11 9
.#………
.#.#######.
.#.#…..#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#…….#.
.#########.
………..
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..

Sample Output
59
6

#include<iostream>using namespace std;char a[25][25];int num[1000]={0};//多组数据输入,将答案存到一个数组中int dfs(int i,int j,int t);int main(){    int w,h,t=0;    while(scanf("%d%d",&w,&h)!=EOF)    {        memset(a,'#',sizeof(a));        if(w==0 && h==0)            break;        else        {            for(int i=1; i<=h;i++)                for(int j=1; j<=w; j++)                    cin>>a[i][j];            for(int i=1; i<=h; i++)                for(int j=1; j<=w; j++)                  if(a[i][j]=='@')//找到人的位置                  {                      a[i][j]='.';                      dfs(i,j,t);                      //printf("%d %d\n",i,j);                      printf("%d\n",num[t]);                  }        }        t++;    }    return 0;}int dfs(int i,int j,int t){    if(a[i][j]=='.')    {       a[i][j]='#';       num[t]++;//计算黑地砖的个数       dfs(i-1,j,t);//向上走       dfs(i+1,j,t);//向下走       dfs(i,j+1,t);//向右走       dfs(i,j-1,t);//向左走       return 1;    }    return 0;}

类型二

1 Prime Ring Problem
2 N皇后问题

运用了优先广度搜索和递归结合,类似于分书问题(王爸爸的实验九)。
对于问题1,可以看成每个位子分配数字,这题位置一已经确定就是1,就不用看位置一了。看后面的位置。数字能否放入就要看是否满足条件,满足后,继续看下一个位置,当达到最后一个位置时,便将结果输出。然后,回溯,将这个位置清空,寻找下一个方案。这对于N皇后问题是一样的思路。

题1

A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.

Note: the number of first circle should always be 1.

Input
n (0 < n < 20).
Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.

You are to write a program that completes above process.

Print a blank line after each case.
Sample Input
6
8
Sample Output
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4

Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2

#include<iostream>#include<cstdio>#include<cmath>using namespace std;int a[20];int vis[20]={0};int n;void dfs(int i);int fun(int tmp,int i);int main(){    int t=0;    while(scanf("%d",&n)!=EOF)    {        t++;        printf("Case %d:\n",t);         a[1]=1;         vis[1]=1;         dfs(2);        printf("\n");    }}void dfs(int i){    int j;    for(j=2; j<=n; j++)    {        int c=(vis[j]==0 && fun(j,a[i-1]));//满足条件:没被使用且与上一个数的和为素数        if(!c)//不满足条件,继续寻找。            continue;        else        {            vis[j]=1;//标记            a[i]=j;        }        if(i==n && fun(a[i],1))//输出条件:到最后一个位子且与1相加为素数        {           for(int k=1; k<=n; k++)           {             if(k==1)                printf("%d",a[k]);             else                printf(" %d",a[k]);           }            printf("\n");        }        else        {            //if(j>n)                //break;            dfs(i+1);//递归,找下一个位置的数        }        vis[j]=0;//回溯,寻找另一个数。    }}int fun(int tmp,int i)//判断与上一位数字相加的和是否为素数{   int m=tmp+i,j,k;   k=sqrt(m);   for(j=2; j<=k; j++)        if(m%j==0)            break;   if(j>k)    return 1;   else    return 0;}

题2

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
Output
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
Sample Input
1
8
5
0
Sample Output
1
92
10

#include <iostream>using namespace std;int num[11]={0}//记录N皇后的放法总数;int b[10];void Try(int i,int n,int t);int check(int i,int j);int main(){    int t;    for(t=1; t<=10; t++)    {        Try(0,t,t);//先放置第一行0        //printf("%d\n",num[t]);    }    int n;    while(scanf("%d",&n)!=EOF)    {        if(n==0)            break;        printf("%d\n",num[n]);    }    return 0;}void Try(int i,int n,int t){    for(int j=0; j<n; j++)    {          if(check(i,j))          {            b[i]=j;            if(i==n-1)                num[t]++;            Try(i+1,n,t);//递归找下一行            b[i]=0;//回溯,寻找下一列          }    }}int check(int i,int j)//检验是否符合条件{    int r;    int c;    for(r=0; r<i; r++)//与前面已经放好的皇后比较    {        c=b[r];        if(j==c)//在同一列            return 0;        if(i+j==r+c)//在一斜线上            return 0;        if(i-j==r-c)//在一斜线上            return 0;    }    return 1;}

二叉树的深度问题

int dfs(node cur,int deep){      int u=0,v=0;    if(cur->left)    {          u=dfs(cur->left,deep+1);       else       {           return deep;        }    if(cur->right)    {           v=dfs(cur->right,deep+1);     else        {           return deep;        }        return max(u,v);}
原创粉丝点击