算法学习笔记——深度优先搜索DFS 2024.5.25

Lanqiao OJ 141

此题是一道比较经典的搜索题目,这里采用深度优先搜索的方法

题目描述

X 星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废。

某坦克需要从 A 区到 B 区去( A,B 区本身是安全区,没有正能量或负能量特征),怎样走才能路径最短?

已知的地图是一个方阵,上面用字母标出了 A,B 区,其它区都标了正号或负号分别表示正负能量辐射区。

例如:

A + - + -

- + - - +

- + + + -

+ - + - +

B + - + -

坦克车只能水平或垂直方向上移动到相邻的区。

输入描述

第一行是一个整数 𝑛,表示方阵的大小,4 ≤ 𝑛 <100。

接下来是 𝑛 行,每行有 𝑛 个数据,可能是 A,B,+,- 中的某一个,中间用空格分开。A,B 都只出现一次。

输出描述

输出一个整数,表示坦克从 A 区到 B 区的最少移动步数。

如果没有方案,则输出 -1。

输入输出样例

示例

输入

5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -

输出

10

解题思路: 

1. 变量定义

我们需要从点A出发,到达点B,因此就要从A的坐标开始进行搜索,直到递归到点B。即递归出口就是参数i、j都等于点B的坐标。我们定义两个pair,分别为A、B,储存点A和点B的坐标。

题目中要求必须+、-交替的前进,那我们不妨定义一个map,让‘+’对应1,让‘-’对应-1,只要两者加起来是0就代表这个位置可以前进,反之则不行。

定义一个char数组a[N][N]来储存输入的地图信息,用一个bool数组b[N][N]来储存路径状态信息,即是否走过该点,如果为true就是走过,为false就是没有走过。这个数组是动态更新的,随着深度搜索的进行随时更新每点的状态。

用int类型变量n来储存数组大小,step来储存当前的步数,ans来储存从A到B的最少步数,step每走一步就要更新一次,ans的值需要再递归出口处更新。

2. 深度优先搜索

深度优先搜索主要分为两个部分,分别是递归出口和递归主体。

递归主体中主要有:修改状态、递归下一层、恢复状态,三个最重要的部分。

递归出口就是坐标到达B,此时更新ans即从A到B的最短步数,ans等于原先的ans与当前步数step中的最小值。

在递归主体中,只有当该点坐标不超过边界范围时才能进入递归搜索。在递归搜索中,分别向上下左右四个方向进行递归搜索,只有当要搜索的点与当前点的‘+’‘-’号相反,或者当前点为A点,或者要搜索的点为‘B’点时才能进入。进入后,如果该点没有被走过,那么就修改该点的状态,让step自增(前进到该点的位置),随后再次调用dfs函数对这个点进行递归。最后恢复该点的状态至没有走过的状态。

注:本方法比较直接,没有进行剪枝等优化,代码写的也比较长,见谅,之后会出代码优化的。

#include <bits/stdc++.h>
using namespace std;
const int N = 101;
char a[N][N];
bool b[N][N];

int n, step = 0, ans = 10000;
pair<int, int> A, B;
map<char,int> m = {{'+', 1}, {'-', -1}};

void dfs(int i, int j)
{
  //递归出口
  if(i == B.first && j == B.second)
  {
    ans = min(ans, step);
    return;
  }
  //递归主体
  if(i!=0 && i!=n+1 && j!=0 && j!=n+1)
  {
    //向下搜索
    if(m[a[i][j]]+m[a[i+1][j]]==0 || a[i][j] == 'A' || a[i+1][j] == 'B')
    {
      if(b[i+1][j] != true)//排除已走过的路径
      {
        b[i+1][j] = true;//修改状态
        step ++;
        dfs(i+1, j);//向下搜索
        b[i+1][j] = false;//恢复状态
        step --;
      }
    }
    //向上搜索
    if(m[a[i][j]]+m[a[i-1][j]]==0 || a[i][j] == 'A' || a[i-1][j] == 'B')
    {
      if(b[i-1][j] != true)//排除已走过的路径
      {
        b[i-1][j] = true;//修改状态
        step ++;
        dfs(i-1, j);//向上搜索
        b[i-1][j] = false;//恢复状态
        step --;
      }
    }
    //向右搜索
    if(m[a[i][j]]+m[a[i][j+1]]==0 || a[i][j] == 'A' || a[i][j+1] == 'B')
    {
      if(b[i][j+1] != true)//排除已走过的路径
      {
        b[i][j+1] = true;//修改状态
        step ++;
        dfs(i, j+1);//向右搜索
        b[i][j+1] = false;//恢复状态
        step --;
      }
    }
    //向左搜索
    if(m[a[i][j]]+m[a[i][j-1]]==0 || a[i][j] == 'A' || a[i][j-1] == 'B')
    {
      if(b[i][j-1] != true)//排除已走过的路径
      {
        b[i][j-1] = true;//修改状态
        step ++;
        dfs(i, j-1);//向左搜索
        b[i][j-1] = false;//恢复状态
        step --;
      }
    }
  }
}
int main()
{
  //输入
  cin >> n;
  for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
    {
      cin >> a[i][j];
      if(a[i][j] == 'A') A = make_pair(i, j);
      if(a[i][j] == 'B') B = make_pair(i, j);
    }
  //深度优先搜索
  b[A.first][A.second] = true;
  dfs(A.first, A.second);
  cout << ans << '\n';
  return 0;
}

 

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值