深度优先搜索(模板使用)

深度优先搜索(模板使用)

模板出处

  • 关于模板出处,来自这里

  • 本文仅通过例题对模板的使用进行说明。

#include
#include
#include
using namespace std;
const int maxn=100;
bool vst[maxn][maxn]; // 访问标记
int map[maxn][maxn]; // 坐标范围
int dir[4][2]={0,1,0,-1,1,0,-1,0}; // 方向向量,(x,y)周围的四个方向

bool CheckEdge(int x,int y){ // 边界条件和约束条件的判断
    if(!vst[x][y] && ...) // 满足条件
    return 1;
    else // 与约束条件冲突
    return 0;
}

void dfs(int x,int y){
    vst[x][y]=1; // 标记该节点被访问过
    if(map[x][y]==G){ // 出现目标态G
        ...... // 做相应处理
        return;
    }
    for(int i=0;i<4;i++){
        if(CheckEdge(x+dir[i][0],y+dir[i][1])) // 按照规则生成下一个节点
            dfs(x+dir[i][0],y+dir[i][1]);
    }
    return; // 没有下层搜索节点,回溯
}
int main(){
    ......
    return 0;
}

例题1:油田

题目说明:

某石油勘探公司正在按疾患勘探地下油田资源,在一片长方形地域中工作。他们首先将该地域划分为许多小正方形区域,然后使用勘探设备分别探测在每一小正方形区域内是否有油。含有油的区域被称为油田。如果两个油田相邻(在水平、垂直或对角线相邻),则它们是相同油藏的一部分。油藏可能非常大并可能包含许多油田(油田的个数不超过100)。你的工作是确定在这个长方形地域中包含多少不同的油藏。

输入:输入文件包含一个或多个长方形地域。每个地域的第1行都有两个正整数m和n(1 \leq m,n \leq 100),表示地域的行数和列数。如果m = 0,则表示输入结束;否则此后有m行,每行都有n个字符。每个字符都对应一个正方形区域,字符*表示没有油,字符@表示有油。

输出:对于每个长方形地域,都单行输出油藏的个数。

算法设计

  • 使用深度优先搜索算法

  • 约束条件为坐标出界限制,对应点是否为油田,标记数组。共3个

#include 
using namespace std;
const int maxn=100;
int m,n;
char str[maxn][maxn];
bool vst[maxn][maxn]; // 访问标记
int dir[8][2]={-1,-1,-1,0,-1,1,
                   0,-1,0,1,1,-1,
                   1,0,1,1};// 方向向量,(x,y)周围的四个方向

bool check(int x,int y);
void dfs(int x,int y);
int main(){
    int count = 0;
    cin >> m >> n;
    memset(vst,false,sizeof(vst));
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> str[i][j];
        }
    }
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if(!vst[i][j] && str[i][j] == '@'){
                dfs(i,j);
                count++;
            }
        }
    }
    cout << count << endl;
    return 0;
}
bool check(int x,int y){ // 边界条件和约束条件的判断
    if(!vst[x][y] && x >= 0 && x < m && y >= 0 && y < n && str[x][y] == '@'){
        return true;
    }
    else{
        return false;
    }
}

void dfs(int x,int y){
    vst[x][y]= true; // 标记该节点被访问过
    for(int i=0;i<8;i++){
        if(check(x+dir[i][0],y+dir[i][1])) // 按照规则生成下一个节点
            dfs(x+dir[i][0],y+dir[i][1]);
    }
    return; // 没有下层搜索节点,回溯
}

输入

5 5
****@
*@@*@
*@**@
@@@*@
@@**@

输出

2

训练3:骑士的旅程

题目描述

骑士决定环游世界,其移动方式如下图。骑士的世界是他生活的棋盘,棋盘面积比普通的 8 × 8 8 \times 8 8×8棋盘小,但它任然是长方形的。你能帮助这个骑士做出旅行计划吗?找到一条道路。骑士每次都进入一个方格,可以在棋盘的任意方格上开始和结束。

输入:输入的第1行包含一个正整数 T T T,表示测试用例的数量。每个测试用例的第1行都包含两个 m m m n ( 1 ≤ m × n ≤ 26 ) n(1 \leq m \times n \leq 26) n(1m×n26),表示 m × n m \times n m×n的棋盘,对行数字标识( 1 ∼ m 1 \sim m 1m),对列用大写字母标识( A ∼ Z A \sim Z AZ)。

输出:每个测试用例的输出都以一个包含“$Scenario #i: $”的行开头,其中i是从1开始的测试用例编号。然后单行输出按字典顺序排列的第1条路径,该路径访问棋盘的所有方块。应通过连接访问方块的名称输出路径,每个方块的名称都由一个大写字母后跟一个数字组成。如果不存在这样的路径,则应该在一行上输出“impossible”。在测试用例之间有个空行。

算法设计

  • 要注意输出数组path和结束标识flag。

  • 由于输出是先行后列,所以写约束条件时,要把棋盘行列互换

  • 约束条件中上界是小于等于,不是小于,是可以等于的。

  • 回溯时要将之前的点标记为false。

#include 
using namespace std;
const int maxn=100;
int m,n;
bool vst[maxn][maxn],flag; // 访问标记
int path[30][2];
int dir[8][2]={-2,-1,-2,1,-1,-2,
               -1,2,1,-2,1,2,
               2,-1,2,1};// 方向向量,(x,y)周围的四个方向
bool check(int x,int y); //约束条件
bool dfs(int x,int y,int step); //深度优先搜索
int main(){
    int T;
    cin>>T;
    for(int k=1;k<=T;k++)
    {
        memset(vst,false,sizeof(vst));
        cin>>m>>n;
        flag=false; //标记是否找到
        cout<<"Scenario #"<<k<<":"<<endl;
        path[0][0]=1;//从(1,1)开始搜索,行:A
        path[0][1]=1;//列1
        vst[1][1]=true;//标记已走过
        if(dfs(1,1,1)){ //初始状态(1,1),步数:1
            for(int i=0;i<m*n;i++)
                cout<<char(path[i][0]+'A'-1)<<path[i][1]; //输出路径(行,列)
            cout<<endl<<endl;
        }
        else
            cout<<"impossible"<<endl<<endl;
    }
    return 0;
}
bool check(int x,int y){ // 边界条件和约束条件的判断
    if(x >= 1 && x <= n && y >= 1 && y <= m && !vst[x][y] && !flag){
        return true;
    }
    return false;
}
bool dfs(int x,int y,int step){
    if(step==n*m) //步数等于棋盘坐标点总数,说明找到了,可以结束
        return flag = true; //已经找到
    for(int i=0;i<8;i++) // 按照规则生成下一个节点
    {
        int x2=x+dir[i][0];
        int y2=y+dir[i][1];
        if(check(x2,y2)) //约束条件
        {
            vst[x2][y2]= true; //标记走过的坐标
            path[step][0]=x2; //记录行
            path[step][1]=y2; //记录列
            dfs(x2,y2,step+1); //步数加1,继续寻找
            vst[x2][y2] = false; //若走不通就回溯
        }
    }
    return flag;
}

训练4:抓住那头牛

题目描述

约翰希望立即抓住逃亡的牛。当前约翰在节点 N N N,牛在节点 K ( 0 ≤ N , K ≤ 100000 ) K(0 \leq N,K \leq 100000) K(0N,K100000)时,他们在同一条线上。约翰有两种交通方式:步行和乘车。如果牛不知道有人在追赶自己,原地不动,那么约翰需要多长时间才能抓住牛?

  • 步行:约翰可以在一分钟内从任意节点 X X X移动到节点 X − 1 X-1 X1 X + 1 X+1 X+1
  • 乘车:约翰可以在一分钟内从任意节点X移动到节点 2 × X 2 \times X 2×X

输入:两个整数 N N N K K K

输出:单行输出约翰抓住牛所需的最短时间(以分钟为单位)。

算法设计

  • 深度搜索的方式和其他题目不太一样,移动的方式有变化,且有一定规律性。
#include
using namespace std;
int n,s;
int dfs(int t); //从n到达位置t的最小步数(深度搜索)
int main(){
    scanf("%d %d",&n,&s);
    if(n == 0){ //如果n = 0则先走一步到1,否则无法乘车
        n++;
        printf("%d",dfs(s)+1);
    }
    printf("%d",dfs(s));
    return 0;
}
int dfs(int t){
    if(t <= n){ //不能向后乘车,所以只能一步步倒退
        return n - t;
    }
    if(t % 2 == 1){ //如果t为奇数,比较从t-1向前1步到t、从t+1向后1步到t哪种步数少
        return min(dfs(t+1)+1,dfs(t-1)+1);
    }
    else{ //如果t为偶数,比较从t/2乘车到t、从n一步步走哪一种步数少
        return min(dfs(t/2)+1,t-n);
    }
}

输入

5 17

输出

4

你可能感兴趣的:(深度优先搜索,搜索算法,c++,算法)