基础dfs搜索讲解

   dfs名为深度优先搜索,顾名思义,是对一个问题按照一种模式不断更新搜索,当到无法搜索时,回复上一状态,继续搜索,直到搜索完成。二魁也做了不少dfs的题,发现都是在一些原有的基础上发生一些变动,比如加上时间限制,比如加上情况判断,又或者各种剪枝,但是本质逃不出dfs的基础问题。当然,有一些变化比较大的,比如匈牙利算法,但是dfs的核心思想不会变。我对于dfs算法的核心理解为对问题的所有可能按照一定的规则进行不断试探,一直到得到结果为止。这也致使了dfs容易超时,但是很实用。后期学习剪枝尤为重要。

 

我把基础类型分为三类:

1:基础的迷宫问题。

2:数据选择问题。

3:石油问题。

 

首先对于第一类,也是最基础的一类。给你一个迷宫,有起点终点,以及可以走的点,不可以走的点。问你能不能从起点走到终点。光说没用,我们依照题讲解一下。

 

Input

 

 

多组测试数据,每组第一行两个正整数,分别为n和m

表示n这个迷宫有n行m列(0<n,m<10)

接着是n行m列,

  '*'表示路

‘#’表示墙

‘S’表示起点

‘T’表示终点

 

 

Output

每组测试数据输出一个结果,如果能从S走到T,输出“YES”,否则输出“NO”

Sample Input

2 2
S#
#T
4 4
S**#
#*#T
#***

Sample Output

NO

YES

 

首先看第一个样例,从s出发无论如何也到不了T,我们可以看出来,但是电脑不会,电脑只会机械的判断,那如何判断呢,思考可以全部遍历的方式,我们发现在任意格都存在可以向左向右向上向下的移动,当所有情况都考虑到时,我们就会得到结果。当都尝试过到达不了时,就输出No。看第二个样例,我们可以这样思考,先向左移动,再向左移动,再向左移动,发现移动不了,再向下移动,发现也移动不了,因为是从左面过来的,再返回左面,再执行右面会陷入死循环,所以走过的路不能回头,再向上移动,遇到边界,也就是说,这是个死胡同,所以要返回上一状态,向下移动,再向左移动,移动不了,再向下移动,再向左移动,再向左移动,再向左移动,遇到边界,移动不了,再向下,也遇到边界,移动不了,从左面来的,所以忽略向左移动,向上,可以,于是向上走,判断到达t,输出yes。

总的来说过程看着比较晦涩,希望大家可以对着代码模拟行走。

 

#include <cstdio>  
#include <cstring>  

using namespace std;  
//定义常量数组,方向为上、下、左、右四个方向  
const int dx[] = {1, 0, -1, 0};  
const int dy[] = {0, 1, 0, -1};  
char c[10][10];//定义字符数组  
int visit[10][10];//定义访问数组,访问标记为1,未访问标记为0  
int n, m, re;//字符数组的行数,列数和返回值  

void dfs(int x, int y){  
    visit[x][y] = 1;  //访问之后加上标记
    if(c[x][y] == 'T'){  //到达终点输出结果
        re = 1;  
        return;  
    }  
    for(int i = 0; i < 4; i++){     //让该格点右下左上移动
        int x1 = x + dx[i];  
        int y1 = y + dy[i];  
        if(x1 >= 0 && y1 >= 0 && x1 < n && y1 < m && visit[x1][y1] == 0 && c[x1][y1] != '#')  //凡是到达边界,或者遇到墙壁,或者走过的格点都不能走
            dfs(x1,y1);  
    }  
}  
int main()  
{  
    while(scanf("%d%d", &n, &m) != EOF){  
        memset(c, '\0', sizeof(c));  
        memset(visit, 0, sizeof(visit));  
        re = 0;  
        for(int i = 0; i < n; i++){  
            getchar();  
            scanf("%s", c + i);  
        }  
        int p,q;  
        //找出起点的坐标  
        for(int i = 0; i < n; i++){  
            for(int j = 0; j < m; j++){  
                if(c[i][j] == 'S'){  
                    p = i;  
                    q = j;  
                    break;  
                }  
            }  
        }  
        dfs(p, q);  
        if(re == 1)  
            printf("YES\n");  
        else  
            printf("NO\n");  
    }  
    return 0;
}


第二类问题呢,是对于数据选择方面,比如填数游戏等等,也给大家举出一个例题。

    hdu1016

 

Prime Ring Problem

 

 

Problem Description

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

 

这道题的意思是给你一个数n,也就是1~n。让你把这些数组成一个环,任意两个相邻的数的和为素数。对于第一个环,第一个位置确定是1,第2个位置可能是2,3,4,5,6,第三个位置也有这些可能,当然,第二个填2,其余的位置都不能填二了。我们考虑到所有的情况就行

 

#include<iostream>
#include<algorithm>
#include<cstring>
#define MM 15

using namespace std;

int used[100],a[100];
int n;

int f(int x)   //判断是否为素数
{
    for(int i = 2;i <= x / 2;i++)
    {
        if(x % i == 0) return 0;
    }
    return 1;
}
void dfs(int pos)
{
    if(pos == n)      //当凑齐n个数时,判断第一个加最后一个是否为素数
    {
        if(f(a[0] + a[n - 1]))
        {
            for(int i = 0;i < n;i++)
            {
                printf("%d",a[i]);
                if(i == n - 1) printf("\n");
                else printf(" ");
            }
        }
    return;
    }
    if(pos == 0)   //把环的第一个位置填入1
    {
        a[pos] = 1;
        dfs(pos + 1);
        return;
    }
    for(int i = 2;i <= n;i++)    //每个位置都可能填入2~n的数
    {
        if(used[i] == 0 && f(i + a[pos - 1]))    //如果这个数从前没有填入过或者填入与相邻的和是素数
        {
            used[i] = 1;         //标注使用过
            a[pos] = i;           //在这个位置填入
            dfs(pos + 1);
            used[i] = 0;          
        }
    }
}
int main()
{
    int k = 1;
    while(scanf("%d",&n) != EOF)
    {
        memset(used,0,sizeof(used));
        used[1] = 1;
        printf("Case %d:\n",k++);
        dfs(0);
        printf("\n");
    }
    return 0;
}

 

 

对于第三种类型,其实本质差不多。

hdu1241

 

Oil Deposits

 

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 35698    Accepted Submission(s): 20691

 

 

Problem Description

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>

using namespace std;

char oil[101][101];
int x[4] = {0, 1, 0, -1};
int y[4] = {1, 0, -1, 0};

int m, n;

void dfs(int a, int b)
{
    if(oil[a][b] != '@' || a < 0 || b < 0 || a > n-1 || b > m - 1)    return;  //遇到边界或者不是油田,返回上一状态
    oil[a][b] = '*';                       //搜索过的油田取消油田标记,不用再搜索
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 4; j++)
        {
            dfs(a + x[i], b + y[j]);            //不断dfs搜索
        }
    }
    return;
}


int main()
{
    int i, j, sl;
    while(cin >> n >> m)
    {
        if(m == 0 && n == 0)    break;
        for(i = 0; i < n; ++i)
            for(j = 0; j < m; ++j)
                cin >> oil[i][j];
        sl = 0;
        for(i = 0; i < n; ++i)         //搜索所有的点
            for(j = 0; j < m; ++j)
            {
                if(oil[i][j] == '@')     //如果这个点是石油,测定这一片的石油情况
                {
                    dfs(i, j);
                    ++sl;
                }
            }
        cout << sl << endl;

    }
    return 0;
}


这些都是很基础的部分,希望大家多加练习。同时对于地图搜索的最短距离问题,比较建议用bfs,后续会再介绍。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值