洛谷 P1825 [USACO11OPEN]Corn Maze S

news/2024/7/15 18:05:01 标签: 算法, 数据结构, 图论

【题目链接】

洛谷 P1825 [USACO11OPEN]Corn Maze S

【题目考点】

1. 广搜 迷宫问题

【解题思路】

输入地图,记录起点、终点。
设方向数组dir,方便取一个格子上下左右的位置。
从起点出发进行广搜,把位置及到该位置的步数打包成一个结点,队列中保存这样的结点。每出队一个结点,访问该结点位置上下左右的四个位置,该位置必须同时满足条件:在地图内、未访问过、不是墙。如果满足条件,则访问该位置,到该位置的步数加1,把该位置和步数打包成结点入队。

本题难点在于处理传送门。
我这里的处理方法是:设tempGate数组,数组的每个元素是一个vector,方便添加数据。vector中的元素类型是数对pair<int, int>,表示一个传送门的位置。tempGate[i]这个vector中保存的是字母为i+'A'的传送门的两个位置。
而后设数对到数对的映射gate,声明方法为:map<pair<int,int>,pair<int,int>> gate;,目的是方便通过传送门A的位置得到传送门B的位置。
遍历tempGate数组,把每个vector中的两个元素之间建立映射关系即可。一个传送门位置作为键, 另一个传送门位置作为值。
在广搜时,如果从当前位置出发走一步到达的位置是传送门,则正常进行,将该位置标记为已访问,打包该位置和步数成一个结点,结点入队。
当出队的位置是传送门时,让牛从当前所在传送门的位置传送到另一个门的位置,从这里开始继续走。
注意这时并不算访问了传送门的出口。
比如走到了传送门 A 1 A_1 A1,访问了 A 1 A_1 A1,等到 A 1 A_1 A1位置出队时,直接跳到另一个传送门位置 A 2 A_2 A2,接着从 A 2 A_2 A2出发继续走,但并没有访问 A 2 A_2 A2
因为也还可能存在路径先走到 A 2 A_2 A2,然后传送到 A 1 A_1 A1,这条路还是应该可以走的。

【题解代码】

解法1:广搜

  • 写法1:使用STL
#include <bits/stdc++.h>
using namespace std;
#define N 305
struct Node
{
	int x, y, s;
	Node(){}
	Node(int a, int b, int c = 0):x(a), y(b), s(c){}//第三项步数如不填,则默认为0 
};
int n, m, sx, sy, ex, ey;
int dir[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
char mp[N][N];
vector<pair<int, int>> tempGate[26];//tempGate[i]:传送门i+'A'的两个位置 
map<pair<int, int>, pair<int, int>> gate;//gate[i]:传送门i对应的位置 
bool vis[N][N];
int bfs()//返回走出迷宫的最少步数 
{
	queue<Node> que;
	vis[sx][sy] = true;
	que.push(Node(sx, sy, 0));
	while(que.empty() == false)
	{
		Node u = que.front();
		que.pop();
		if(u.x == ex && u.y == ey)//走出迷宫 
			return u.s;
		if(isupper(mp[u.x][u.y]))//如果u位置是传送门,则通过传送门 
		{
			pair<int, int> t = gate[make_pair(u.x, u.y)];
			u.x = t.first, u.y = t.second;	
		}
		for(int i = 0; i < 4; ++i)
		{
			int x = u.x+dir[i][0], y = u.y+dir[i][1];
			if(x >= 1 && x <= n && y >= 1 && y <= m && vis[x][y] == false && mp[x][y] != '#')
			{
				vis[x][y] = true;
				que.push(Node(x, y, u.s+1));
			}
		}
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
		{
			cin >> mp[i][j];
			if(mp[i][j] == '@')
				sx = i, sy = j;//起点 
			else if(mp[i][j] == '=')
				ex = i, ey = j;//终点 
			else if(isupper(mp[i][j]))//如果是大写字母
				tempGate[mp[i][j]-'A'].push_back(make_pair(i, j));	 
		}
	for(int i = 0; i < 26; ++i)//构建映射gate 
	{
		if(tempGate[i].size() > 0)//同一种传送门只能存在两个 
		{
			gate[tempGate[i][0]] = tempGate[i][1];//一个传送门位置作为键, 另一个传送门位置作为值 
			gate[tempGate[i][1]] = tempGate[i][0];
		}
	}
	cout << bfs();
	return 0;
}
  • 写法2:C风格
#include <iostream>
using namespace std;
#define N 305
struct Pair
{
	int x, y;
};
int n, m, sx, sy, ex, ey;
int dir[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
char mp[N][N];
int step[N][N];//step[i][j]:到(i, j)的步数 
Pair tempGate[26][2];//tempGate[i]:传送门i+'A'的两个位置 
int tgn[26];//tgn[i]:传送门i+'A'已经出现的个数 
bool vis[N][N];
Pair getAnotherGate(Pair p)//已知传送门在p位置,求传送门的另一个传送门的位置 
{
	int ind = mp[p.x][p.y]-'A';
	if(p.x == tempGate[ind][0].x && p.y == tempGate[ind][0].y)
		return tempGate[ind][1];
	else
		return tempGate[ind][0];
}
int bfs()//返回走出迷宫的最少步数 
{
	Pair que[N*N];
	int head = 0, tail = 0;//队列 
	vis[sx][sy] = true;//还应该有step[sx][sy] = 0;全局变量默认为0,就不写了。 
	++tail;
	que[tail].x = sx, que[tail].y = sy;//(sx, sy)入队 
	while(head != tail)
	{
		Pair u = que[++head];//出队 
		if(u.x == ex && u.y == ey)//走出迷宫 
			return step[u.x][u.y];
		if(mp[u.x][u.y] >= 'A' && mp[u.x][u.y] <= 'Z')//如果u位置是传送门,则通过传送门 
		{
			int st = step[u.x][u.y];//把到传送门起点的步数转移到终点 
			u = getAnotherGate(u);
			step[u.x][u.y] = st;
		}
		for(int i = 0; i < 4; ++i)
		{
			int x = u.x+dir[i][0], y = u.y+dir[i][1];
			if(x >= 1 && x <= n && y >= 1 && y <= m && vis[x][y] == false && mp[x][y] != '#')
			{
				vis[x][y] = true;
				step[x][y] = step[u.x][u.y]+1;
				++tail;
				que[tail].x = x, que[tail].y = y;
			}
		}
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
		{
			cin >> mp[i][j];
			if(mp[i][j] == '@')
				sx = i, sy = j;//起点 
			else if(mp[i][j] == '=')
				ex = i, ey = j;//终点 
			else if(mp[i][j] >= 'A' && mp[i][j] <= 'Z')//如果是大写字母
			{
				int ind = mp[i][j]-'A';
				tempGate[ind][tgn[ind]].x = i, tempGate[ind][tgn[ind]].y = j;
				tgn[ind]++;
			}
		}
	cout << bfs();
	return 0;
}

http://www.niftyadmin.cn/n/117848.html

相关文章

洗地机哪个牌子好?口碑最好的洗地机

选择洗地机&#xff0c;最关键的当然是清洁力度啦&#xff0c;这就要看洗地机的吸力如何了&#xff0c;一般情况下&#xff0c;吸力越大&#xff0c;越能够吸附顽固污渍&#xff0c;清洁力度就越好。然后杀菌功能也是必不可少的&#xff0c;毕竟是要清洁整个家的地面卫生&#…

Vue3学习笔记

一、Ref ref, isRef, shallowRef, triggerRef, customRef ref返回的是es6的一个class类&#xff0c;取值和修改都要加上.valueref 和 shallowRef不能一起写&#xff0c;会引起shallowRef的视图更新ref shallowRef triggerRef <template><div class"home&quo…

localStorage线上问题的思考

一、背景&#xff1a; localStorage作为HTML5 Web Storage的API之一&#xff0c;使用标准的键值对&#xff08;Key-Value,简称KV&#xff09;数据类型主要作用是本地存储。本地存储是指将数据按照键值对的方式保存在客户端计算机中&#xff0c;直到用户或者脚本主动清除数据&a…

【自用】SpringBoot项目通用类整理

文章目录全局Json序列化Controller日志切面全局异常拦截GlobalExceptionHandlerApiResultBusinessExceptionResponseEntityUtil全局返回体包装MP自动填充接口文档配置类自定义Async异步线程池本文主要整理各类项目中通用的配置类、工具类&#xff0c;便于复查自用。 全局Json序…

Linux命令·rmdir

今天学习一下linux中命令&#xff1a; rmdir命令。rmdir是常用的命令&#xff0c;该命令的功能是删除空目录&#xff0c;一个目录被删除之前必须是空的。&#xff08;注意&#xff0c;rm - r dir命令可代替rmdir&#xff0c;但是有很大危险性。&#xff09;删除某目录时也必须具…

JAVA中公平锁和非公平锁有什么区别?

从公平的角度来说,Java 中的锁总共可分为两类:公平锁和非公平锁。但公平锁和非公平锁有哪些区别? 正文 公平锁:每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的,最前面的线程总是最先获取到锁。非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则…

【Java学习笔记】3.Java 基础语法

Java 基础语法 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff…

0098 Mysql01

1.登录Mysql mysql -uroot -p密码 2.Mysql常用命令 退出:exit 查看mysql有哪些数据库&#xff1a;show databases;(以分号结尾) 选择使用某个数据库&#xff1a;use sys; (表示正在使用一个名叫sys得数据库) 创建数据库&#xff1a;create database bjpowernode; 查看某个数…