[递归,方向判断] 左手定则

左手定则

题目描述

玩过RPG(尤其是国产RPG)的童鞋都应该对迷宫这种神棍的设定深恶痛绝,尤其是当你转了半小时之后发现回到了原地,这种感觉真是无比的痛苦。。。万一游戏还中途崩溃了那真是连掀桌子、砸键盘、摔鼠标的心都有了……
经过无数次的TRIAL AND ERROR之后,玩家终于下定决心认定迷宫存在的意义就是延长游戏时间,SO,他决定借鉴著名的左手定则(就是在每一个路口,我们都选择最左边的方向,左转的优先级最高,其次为向前,最后为右转,如果实在走进了一个死胡同,那就连续右转两次,回头向后走。稍微研究一下这种走迷宫的方法,我们就发现在这个过程中,事实上我们的左手可以始终放在墙上。)对迷宫进行探索。
但是呢,左手定则并不能保证遍历到迷宫中的每一个点。悲剧的是,某项重要的通关道具被放在了这个迷宫中……幸运的是,游戏迷宫的地图可以绘制出来,现在请求你的帮助。对于一个给定的地图,他想知道是不是能够通过左手定则取得这件道具。

关于输入

多组数据。
对于每组数据,第一行有两个整数N,M代表接下来有n行字符串,每行m个字符,其中0 < n <= 100, 0 < m <= 100,表示一个n × m的迷宫。
其中‘#’表示墙,‘S’表示起点,‘T’表示道具,‘.’表示空地。
接下来是一个方向(NSWE),表示起始面向的方向。
数据保证最外一圈都是墙。

关于输出

对于每组数据输出一行。‘YES’表示可以到达,‘NO’表示无法到达。

例子输入
8 10
##########
#...T....#
#.####...#
#.#..#.#.#
#.#....#.#
#.####.#.#
#......#S#
##########
N
8 10
##########
#........#
#.####...#
#.#T.#.#.#
#.#....#.#
#.####.#.#
#......#S#
##########
N
8 10
##########
#....#...#
#..#.#...#
#..#.....#
#..#.#S###
#..#.#...#
#....#T..#
##########
N
例子输出
YES
NO
YES
提示信息

E东
S南
W西
N北
不会原地转圈

解题分析

这个问题可以用深度优先搜索(DFS)的实现,主要用于解决迷宫探索问题,特别是在迷宫中寻找特定的目标。在这个问题中,我们使用了左手定则,即在每个路口,我们都选择最左边的方向,左转的优先级最高,其次为向前,最后为右转,如果实在走进了一个死胡同,那就连续右转两次,回头向后走。

要解决的问题:

1. **迷宫和访问状态的存储**

```c
char maze[105][105];
int visited[105][105][4] = {0};
```
`maze[105][105]` 是一个二维字符数组,用于存储迷宫的地图。迷宫中的墙壁由字符 '#' 表示,起点由 'S' 表示,目标(道具)由 'T' 表示,空地由 '.' 表示。

`visited[105][105][4]` 是一个三维整数数组,用于记录在四个方向(北、东、南、西)上每个位置是否被访问过。通过这个数组,我们可以避免在搜索过程中重复访问同一位置的同一方向。

2. **方向的表示**

```c
int dp[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
```
`dp[4][2]` 是一个二维整数数组,用于表示四个方向(北、东、南、西)的位移。数组的第一维代表方向,第二维代表在 x 和 y 方向上的位移。例如,`{-1, 0}` 代表向北移动,`{0, 1}` 代表向东移动,`{1, 0}` 代表向南移动,`{0, -1}` 代表向西移动。

3. **位置的更新**

```c
void change(int *x, int *y, int dir, int dir_to){
    *x += dp[(dir + dir_to + 4) % 4][0];
    *y += dp[(dir + dir_to + 4) % 4][1];
}
```
`change` 函数用于根据当前的方向和要转向的方向,更新当前的位置。这里使用了 `(dir + dir_to + 4) % 4` 来计算新的方向,模4是为了循环0-3,这里注意与dp数组移动坐标相对应,只需举一个例子向北,然后就可以根据这个例子去确定方向和表示,然后通过 `dp` 数组来获取在 x 和 y 方向上的位移。

4. **深度优先搜索**

```c
int dfs(int x, int y, int dir){
    ...
    for(int i = -1; i < 3; i++){
        int dx = x, dy = y;
        change(&dx, &dy, dir, i);
        ...
        if(maze[dx][dy] != '#'){
            visited[dx][dy][(dir + i + 4) % 4] = 1;
            return dfs(dx, dy, (dir + i + 4) % 4);
        }
    }
    ...
}
```
`dfs` 函数是一个深度优先搜索函数,它会尝试从当前位置向四个方向移动,并且优先选择左边的方向。如果找到了道具,就返回1,表示可以到达;如果四个方向都尝试过了还没找到道具,就返回0,表示无法到达。在这个函数中,我们使用了 `change` 函数来更新位置,并使用了 `visited` 数组来记录访问状态。

5. **主函数**

```c
int main() {
    ...
    if(dfs(xi, yi, Dir0)) printf("YES\n");
    else printf("NO\n");
    ...
}
```
`main` 函数首先读取输入数据,然后调用 `dfs` 函数进行搜索。如果 `dfs` 函数返回1,就输出 'YES',表示可以通过左手定则找到道具;如果 `dfs` 函数返回0,就输出 'NO',表示无法通过左手定则找到道具。

这个程序的主要思路是,我们始终尝试向左边的方向移动,如果左边的方向是墙,就尝试向前移动,如果前面也是墙,就尝试向右移动,如果右边也是墙,就回头。这样,我们就可以遍历迷宫中的所有可能路径,而且不会重复绕圈圈。

代码实现
#include <stdio.h>
#include <string.h>

char maze[105][105];
int visited[105][105][4] = {0};
int dp[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int N, M;

void change(int *x, int *y, int dir, int dir_to){
    *x += dp[(dir + dir_to + 4) % 4][0];
    *y += dp[(dir + dir_to + 4) % 4][1];
}

int dfs(int x, int y, int dir){
    if(maze[x][y] == 'T'){
        return 1;
    }
    for(int i = -1; i < 3; i++){
        int dx = x, dy = y;
        change(&dx, &dy, dir, i);
        if(dx >= 0 && dx < N && dy >= 0 && dy < M){
            if(visited[dx][dy][(dir + i + 4) % 4]) return 0;
            else if(maze[dx][dy] != '#'){
                visited[dx][dy][(dir + i + 4) % 4] = 1;
                return dfs(dx, dy, (dir + i + 4) % 4);
            }
        }
    }
    return 0;
}

int main() {
    int xi, yi;
    while(scanf("%d %d", &N, &M) != EOF){
        memset(visited, 0, sizeof(visited));
        getchar(); // 吸收换行符
        for(int i = 0; i < N; i++){
            for(int j = 0; j < M; j++){
                scanf(" %c", &maze[i][j]); // 注意%c前的空格,用于吸收空白字符
                if(maze[i][j] == 'S'){
                    xi = i; yi = j;
                }
            }
        }
        char firstDir; 
        int Dir0;
        scanf(" %c", &firstDir); // 注意%c前的空格
        switch(firstDir){
            case 'N':
                Dir0 = 0;
                break;
            case 'E':
                Dir0 = 1;
                break;
            case 'S':
                Dir0 = 2;
                break;
            case 'W':
                Dir0 = 3;
                break;
        }
        if(dfs(xi, yi, Dir0)) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
进一步拓展

在使用 scanf("%c", &maze[i][j]); 读取字符时,%c 会读取所有字符,包括空格、制表符和换行符。这意味着如果在输入中有任何空白字符(如输入两个字符之间的空格或上一个输入后的换行符),%c 会将其作为有效字符读取,这可能会导致输入不符合预期。

为了避免这个问题,scanf(" %c", &maze[i][j]); 中的空格告诉 scanf 忽略所有标准空白字符(空格、制表符、换行符等),直到遇到第一个非空白字符。这样,它只会读取并存储真正的有效字符,而忽略所有前导的空白字符。

例如,在读取矩阵或字符网格时,这种处理方式尤其重要,因为它确保了只有实际的数据字符(非空白字符)被读取并存储在数组中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/213634.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

粉丝提问:岗位与描述不一致,小公司感觉学不到东西,工作内容就是调试,想辞职

0、粉丝问题&#xff1a; 大哥&#xff0c;我毕业已经工作两个月了&#xff0c;在一家小公司&#xff0c;岗位和描述的不一致&#xff0c;感觉就像调试一样&#xff0c;写代码的机会很少也没人带&#xff0c; 我想转嵌入式&#xff0c;您有什么建议的方向吗&#xff0c;或者是…

MathType公式编辑器安装教程

一、下载 MathType7是一款可以帮助用户快速完成数学公式编辑的应用软件&#xff0c;这款软件适合在进行教育教学、科研机构、论文写作的时候使用。我们可以直接通过这款软件来获取到大量数学上使用到的函数、数学符号等内容&#xff0c;然后使用这些内容来完成公式编辑。 …

玩转大数据4:大数据的崛起与应用领域探索

图片来源网络 引言 在当今数字化时代&#xff0c;大数据正以前所未有的速度和规模崛起。大数据的出现不仅改变了企业和组织的经营模式&#xff0c;也对我们的社会生活带来了深刻的影响。Java作为一种广泛使用的编程语言&#xff0c;在大数据领域发挥着重要的作用。本文将重点…

自动驾驶学习笔记(十三)——感知基础

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 传感器 测距原理 坐标系 标定 同…

初识Linux:保姆级教学,让你一秒记住Linux中的常用指令!

文章目录 前言一、LInux的背景及发展史二、Linux下的基本指令1、ls指令2、pwd指令3、cd指令4、touch指令5、mkdir指令&#xff08;重要&#xff09;6、tree指令7、rmdir指令和rm指令&#xff08;重要&#xff09;8、man指令&#xff08;重要&#xff09;9、cp指令&#xff08;重…

操作PDF相关的工具,EPUB转PDF,golang

unipdf 安装依赖 go get github.com/unidoc/unipdf/v3 示例代码 https://github.com/unidoc/unipdf-examples 获取KEY 登录 https://cloud.unidoc.io/ 注册账号&#xff0c;生成 KEY&#xff0c;但是需要收费。 chromedp 使用Golang编写&#xff0c;主要功能是调用浏览器内…

【面试攻略】Oracle中blob和clob的区别及查询修改方法

大家好&#xff0c;我是小米&#xff0c;欢迎来到小米的技术小屋&#xff01;今天我们要一起来聊聊一个在面试中常常被问到的问题——“Oracle中Blob和Clob有啥区别&#xff0c;在代码中怎么查询和修改这两个类型的字段里的内容&#xff1f;”别急&#xff0c;跟着小米一步步揭…

Android11适配已安装应用列表

Android11适配已安装应用列表 之前做过已安装应用列表的适配&#xff0c;最近国内版SDK升级到33和隐私合规遇到很多问题&#xff0c;于是把已安装应用列表记录一下&#xff1a; 1、在Android11及以上的适配&#xff1a; package com.example.requestinsttallapplistdemoimpo…

K210开发板之VSCode开发环境使用中添加或删除文件(编译失败时)需要注意事项

在最初开始接触&#xff0c;将VScode和编译环境搭载好后&#xff0c;就开始运行第一个程序了&#xff0c;为了后续方便开发测试&#xff0c;这里我自己对照官方提供的例子&#xff0c;自己调试&#xff0c;写了一个简单的文件系统 后续&#xff0c;所有关于开发的源文件都在...…

Sun Apr 16 00:00:00 CST 2023格式转换

Date date new Date(); log.info("当前时间为:{}",date); //yyyy-MM-dd HH:mm:ss SimpleDateFormat sdf new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS); String dateTime s…

Git Bash环境下用perl脚本获取uuid值

在Linux环境下&#xff0c;比如在ubuntu就直接有uuidgen命令直接获取uuid值。在Windows环境下常用的git bash中没有对应的命令&#xff0c;略有不便。这里用脚本写一个uuidgen&#xff0c;模拟Linux环境下的uuidgen命令。 #! /usr/bin/perl use v5.14; use Win32;sub uuidGen {…

Springboot启动原理解析

我们开发任何一个Spring Boot项目&#xff0c;都会用到如下的启动类 SpringBootApplication public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} } 从上面代码可以看出&#xff0c;Annotation定义&#x…

大数据技术学习笔记(七)—— Zookeeper

目录 1 Zookeeper 概述1.1 Zookeeper 定义1.2 Zookeeper 工作机制1.3 Zookeeper 特点1.4 数据结构1.5 应用场景 2 Zookeeper 安装3 客户端命令行操作4 Zookeeper 的 Java 客户端操作4.1 IDEA 环境搭建4.2 初始化 ZooKeeper 客户端4.3 创建子节点4.4 获取子节点4.5 判断Znode是否…

详解SpringAop开发过程中的坑

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

Kubernetes实战(六)-多系统架构容器镜像构建实战

1 背景 最近在一个国产化项目中遇到了这样一个场景&#xff0c;在同一个 Kubernetes 集群中的节点是混合架构的&#xff0c;即其中某些节点的 CPU 架构是 x86 的&#xff0c;而另一些节点是 ARM 的。为了让镜像在这样的环境下运行&#xff0c;一种最简单的做法是根据节点类型为…

使用Java语言实现字母之间的大小写转换

这个类的作用为实现字母之间的大小写转换&#xff0c;通过加减32来完成。 输入的代码 import java.util.Scanner; public class WordChangeDemo {public static void main(String[] args){try (Scanner in new Scanner(System.in)) {System.out.println("请输入您要进…

springboot单元测试关闭日志

在logback中关闭日志 在test目录下新建文件夹resources&#xff0c;新增文件logback-test.xml文件 在logback-test.xml 文件中&#xff0c;添加内容&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <configuration><include resourc…

接口测试的简介及测试用例的设计

一&#xff0c;什么是接口 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 二&#xff0c;接口…

《python每天一小段》--(1)与GPT-3.5-turbo 模型进行对话

对话如图&#xff1a; 配置环境变量 APIKey如何获得这边不做说明 在Windows操作系统中&#xff0c;你可以按照以下步骤设置环境变量&#xff1a; 打开“控制面板”。在控制面板中&#xff0c;选择“系统和安全”。选择“系统”。在系统窗口中&#xff0c;选择“高级系统设置”…

6-3 求3*3整数矩阵对角线元素之和

#include<stdio.h>int main(){int a[3][3],sum0;int i ,j;printf("输入元素&#xff1a;\n");for(i0;i<3;i)for(j0;j<3;j)scanf("%d",&a[i][j]);for(i0;i<3;i)sumsuma[i][i];printf("总和为&#xff1a;%d",sum);return 0;}
最新文章