洛谷P1189题解

看原题 戳这里

​ 很容易想到朴素搜索的一道地图搜索题,以方向为搜索深度,记录当前深度的坐标再用dfs扩展方向。

​ 略微不同的是一般地图dfs是可以四个方向搜索,与之对应的是四个方向搜索的dfs深度一般不会太高,但这道题是以固定方向,不固定长度。最坏情况下扩展方向长度有50,而方向深度有1000,所以朴素dfs一定会爆。

  • 解决方法:记忆化搜索

    ​ 在搜索的路途中,有很多情况被重复搜索过多次导致复杂度爆炸。所以我们要做的就是开一个标记数组防止一个状态搜完又搜浪费时间。具体就是开一个 vis[op][x][y]数组表示在完成第op步方向后,map[x][y]是否之前有被搜索到达过。

    • 小技巧,方向的存储读取

      将上下左右方向抽象成数字并用数组存下来,那么就能方便的遍历上下左右每个方向

      //方便的将string类型的方向转换成数字,下标0, 1, 2, 3对应北、东、南、西
      int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
      

      x + dx[0], y+dy[0]就表示在x,y这个点上往北走一步得到的坐标。那么容易知道怎么遍历所有方向:

      //遍历方向
      for (int i = 0; i < 4; i++){
          int nx = x + dx[i];
          int ny = y + dy[i];
          ...
      }
      

      不过不同的是,这题方向是固定的,步长不固定,其实略微做修改就好了。

      //遍历步长
      for (int i = 1;; i++){
          int nx = x + dx[固定的方向] * i;
          int ny = y + dy[固定的方向] * i;
          ...
      }
      
    • 大坑点

      ​ 标记数组在什么时候再标记为走过呢?答:应该先递归调用下一步的dfs方程,再把当前这一步标记为走过。具体原因看代码

上代码

#include <iostream>
#include <string.h>
#define N 60
//存地图
char map[N][N];
//按照题目的变量定义
int r, c, n;
//方便的将string类型的方向转换成数字,下标0, 1, 2, 3对应北、东、南、西
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dr[1010];
bool vis[1010][N][N];

void dfs(int x, int y, int op){
    //大坑点,因为dfs的op定义是即将对第op个方向操作,所以标记数组是要减去一位的,是vis[op-1]而不是vis[op]
    if (op > n || vis[op - 1][x][y])
        return;
    
    //遍历在第op个方向上可以走的长度
    for(int i = 1; ; i++){
        //使用方向的快捷变化拿到走i步长的坐标
        int nx = x + dx[dr[op]] * i;
        int ny = y + dy[dr[op]] * i;
        //判断越界以及是否撞墙
        if (ny >= 1 && nx >= 1 && ny <= c && nx <= r && map[nx][ny] != 'X'){
            //这里可以直接对map操作,而不用再开个标记map存是否最终可以到这个点
            if (op == n)
                map[nx][ny] = '*';
            //注意大坑点,必须要先dfs再标记
            dfs(nx, ny, op + 1);
            vis[op][nx][ny] = true;
        }
        else break;
    }
}

int main(){

    //读入操作,没什么好说的,实验证明读入优化再好也没优化dfs核心来的快
    //-------------------------我是分割线-----------------
    int x = 0, y = 0;
    scanf("%d%d", &r, &c);
    for (int i = 1; i <= r; i++)
        scanf("%s", map[i] + 1);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++){
        char s[10];
        scanf("%s", s);
        if (s[0] == 'N')
            dr[i] = 0;
        else if (s[0] == 'E')
            dr[i] = 1;
        else if (s[0] == 'S')
            dr[i] = 2;
        else if (s[0] == 'W')
            dr[i] = 3;
    }
    //找初始位置在哪
    for (int i = 1; i <= r; i++)
        for (int j = 1; j <= c; j++)
            if (map[i][j] == '*'){
                //记得把初始位置设置成啥都没有
                map[i][j] = '.';
                x = i;
                y = j;
                i = r;
                j = c;
            }
    //-------------------------我是分割线-----------------

    //从初始位置和第一个方向开始搜索
    dfs(x, y, 1);

    for (int i = 1; i <= r; i++)
        printf("%s\n", map[i] + 1);
    return 0;  
}
posted @ 2022-05-02 20:28  可可苦力  阅读(120)  评论(0)    收藏  举报