2020安徽省大学生程序设计大赛题解——E 收集圣物

E 收集圣物

在一个策略游戏中,僧侣单位可以收集圣物,放入修道院中,以生产黄金。因此,圣物对赢得游戏的胜利很重要。
某个僧侣正处于一个长为n 宽为m 的矩形迷宫中,迷宫的四周都是墙壁,不可移动。迷宫中圣物可能有若干个,为了降低游戏难度,僧侣只需要发现任意一个圣物,就能完成任务。
迷宫有若干扇门。同时,在迷宫的一些地方会有钥匙。每一把钥匙都只能使用一次,每一次可以开一扇门,即:一旦你通过一扇门后,门将自动关闭,如果想要再次打开这扇门,你需要再使用一把钥匙去打开它,通过之后门还将自动关闭。
输入这个迷宫,输出最快多少秒能够发现一个圣物。僧侣移速缓慢,每秒只能够向上下左右四个方向各移动一个单位。不过开门以及拾取钥匙不耗费时间。
如果僧侣无法收集任何圣物,输出“Bug Maze ! ! ! ”。

题目

标签

队列,图的BFS,状态压缩,状态转移

分析

类似于迷宫求最短路径问题,与传统问题不同的是,本题含有多个终点,并且前往终点的路上增加了门钥匙这一条件。另外,本题可能无解,因此需要对传统的判断条件加以改变。

考虑传统问题的解决办法,最直接思路是图的BFSBFSBFS算法。我们设置设置一个d[i][j]d[i][j]d[i][j]的二维数组来存储从起点到坐标(i,j)(i,j)(i,j)的最短距离,通过不断的遍历进行寻迹,并把每个状态与最优状态进行比较,以确定最终的最短路径。

因为本题新增了元素门钥匙,原有的算法需要做出相应的改变。最直观的变化体现在d[i][j]d[i][j]d[i][j]上,因为不符合门钥匙要求的最短路径不具有讨论的意义。

为了解决这个问题,我们需要再加入两个状态,用四维数组∗∗∗∗d****d∗∗∗∗d来对门钥匙的情况进行记录,每个坐标现有四个状态(x,y,cnt,state)(x,y,cnt,state)(x,y,cnt,state),d[x][y][cnt][state]d[x][y][cnt][state]d[x][y][cnt][state]表示在去xxx,yyy这个位置,当前有cntcntcnt把钥匙,当前地图中钥匙的状态为statestatestate时,最小路径是多少。通过增加的这两个状态来解决是否有钥匙开门以及不能重复拾取钥匙的问题。

对于数据集钥匙数量小于等于101010的要求,我们将钥匙的状态表示成二进制000000000000000000000000000000,即十个000,如果我们转移到下一个位置的时候,拿到了之前初始化编号的第一把钥匙,对应位置statestatestate就从000000000000000000000000000000变成000000000100000000010000000001,即000表示该编号位置的钥匙没被拿过,111表示该编号位置的钥匙被拿过了。

数组charg[26][26]char \ g[26][26]char g[26][26]存储用户输入的迷宫符号,首先逐行读入迷宫的各状态,并分别存储起点坐标和钥匙编号,将每个钥匙的坐标即编号存入map<PII,int>mpmap<PII, int> mpmap<PII,int>mp,然后从起点开始BFSBFSBFS算法。

值得注意的是,这里的∗∗g**g∗∗g理解为由字符串组成的数组。为了匹配本题的需求,我们把字符串的起始位置定义为111,因此数据的读入部分如下:

for (int i = 1; i <= n; i++) cin >> g[i] + 1;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)if (g[i][j] == 'S') sx = i, sy = j;    //标记起点else if (g[i][j] == 'K') {mp[make_pair(i, j)] = cnt;cnt++;}

与传统迷宫问题最大的不同在于后面的部分,即状态压缩与状态转移。我们已经完成了状态压缩,即用∗∗∗∗d****d∗∗∗∗d表示当前路径长度和钥匙的状态,现在考虑状态转移。

我们在遇到KKK即钥匙时,首先判断这个钥匙的编号ididid,然后再看这个钥匙是否被拿过,即判断当前statestatestate的第ididid位是否是111,如果是1,则将当前位置当空地处理。如果是000,则说明该钥匙没被吃过,当前钥匙数+1+1+1,并将当前statestatestate的第ididid位标记为一。

然后是BFSBFSBFS算法:
找到从起点到终点的最短路径其实就是一个建立队列的过程:

  1. 从起点开始,将其加入队列,设置距离为000
  2. 从队列首端取出位置,将从这个位置能够到达的位置加入队列,并且让这些位置的距离为上一个位置的距离加上111
  3. 一轮探索至无法继续向队列中添加元素,即当前所在点的四周情况全部考虑完成后,循环第二步,直到将圣物添加到队列中。说明此时已经找到了最短路径

参考答案(C++)

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;   //存坐标
const int N = 30;
const int dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 };//人行进的方向
int n, m;
char g[26][26];               //迷宫
int d[26][26][11][1030];      //到当前状态的最小距离
int cnt = 0;                  //当前的钥匙个数
map<PII, int> mp;             //记录钥匙的编号
bool vaild(int x, int y) { return x > 0 && x <= n && y > 0 && y <= m; }//判断是否在迷宫中
struct Node {             //记录当前的状态int x, y, cnt, state; //x,y表示当前的位置 cnt表示当前有多少把钥匙 state表示当前哪些位置的钥匙被吃过了Node() {}Node(int x, int y, int cnt, int state) :x(x), y(y), cnt(cnt), state(state) {}
};
int bfs(int sx, int sy) { //从人当前开始广度优先搜索遍历memset(d, -1, sizeof(d));queue<Node> Q;Q.push({ sx,sy,0,0 }); //放入第一个人的当前状态入队d[sx][sy][0][0] = 0;while (!Q.empty()) {auto t = Q.front();Q.pop();int x = t.x, y = t.y, cnt = t.cnt, state = t.state;int dd = d[x][y][cnt][state];for (int i = 0; i < 4; i++) {int nx = x + dx[i], ny = y + dy[i];if (!vaild(nx, ny)) continue;if (g[nx][ny] == '#') continue;if (g[nx][ny] == 'E') return dd + 1;if (g[nx][ny] == '.') {d[nx][ny][cnt][state] = dd + 1;Q.push({ nx,ny,cnt,state });}if (g[nx][ny] == 'K') {PII now = { nx,ny };int id = mp[now];if (state & (1 << id)) {//表示state的第id位是1d[nx][ny][cnt][state] = dd + 1;Q.push({ nx,ny,cnt,state });}else {d[nx][ny][cnt + 1][state | (1 << id)] = dd + 1;Q.push({ nx,ny,cnt + 1,state | (1 << id) });}}if (g[nx][ny] == 'D' && cnt > 0) {d[nx][ny][cnt - 1][state] = dd + 1;Q.push({ nx,ny,cnt - 1,state });}}}return -1;
}
void solve() {cin >> n >> m;                                 //输入迷宫的行列for (int i = 1; i <= n; i++) cin >> g[i] + 1;  //输入迷宫各状态,由于数据是一行一行的给的,故int sx = 0, sy = 0;                            for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)if (g[i][j] == 'S') sx = i, sy = j;    //标记人else if (g[i][j] == 'K') {mp[make_pair(i, j)] = cnt;cnt++;}int ans = bfs(sx, sy);if (ans == -1) cout << "Bug Maze!!!";else cout << ans << '\n';
}
int main() {solve();return 0;
}

2020安徽省大学生程序设计大赛题解——E 收集圣物相关推荐

  1. 2020安徽省大学生程序设计大赛题解——A数字排列

    2020安徽省大学生程序设计大赛题解--A数字排列 A 数字排列 给出两个整数A 和 B,可以重新排列A 得到新的数字C(不能有前导0).求在小于等于B的情况下,C的最大值是多少.如果不存在输出-1. ...

  2. 2020安徽省大学生程序设计大赛题解——K 农夫打狼

    2020安徽省大学生程序设计大赛题解--K 农夫打狼 K 农夫打狼 题解 标签 排序,动态规划 分析 图K−1本题的一个样例,彩色十字为起点和终点图K-1 \ \ \ \ 本题的一个样例,彩色十字为起 ...

  3. 2020安徽省大学生程序设计大赛题解——F 跳蛙出行

    2020安徽省大学生程序设计大赛题解--F 跳蛙出行 F 跳蛙出行 池塘里有n片荷叶排成一行,有一只青蛙在上面跳跃.但是,这只青蛙是只不同寻常的青蛙,它每跳一次,只能从一片荷叶跳到相邻的荷叶上,并且, ...

  4. 2020安徽省大学生程序设计大赛题解——J 飞奔的战士

    2020安徽省大学生程序设计大赛题解--J 飞奔的战士 J 飞奔的战士 题目 众所周知, T e u t o n i c K n i g h t Teutonic Knight TeutonicKni ...

  5. “Shopee杯” e起来编程暨武汉大学2020年大学生程序设计大赛决赛(重现赛)

    比赛链接 文章目录 A题 A Simple Problem about election 题目描述 题解: 代码: D题 Deploy the medical team 题意: 题解: 代码: F题 ...

  6. 2017年“华信智原杯”安徽省大学生程序设计大赛C题-刷票

    C. 刷票 题目描述: 有一个选秀比赛,节目组按照观众的投票情况决定选手的去留.为了给旗下 艺人造势,A 公司收买了一批水军来刷票.已知现在有 n 名选手同台竞争,依次 编号 1-n,A 公司的艺人编 ...

  7. 2022年安徽省机器人大赛——程序设计赛道 第十三届安徽省大学生程序设计大赛————I 玩捉迷藏

    (建议直接观看后面图片,图片更生动哦) 首先看这个题目,这个题目也是比较简单得,最主要就是读懂这个题目,因为我感觉这个题目还是比较绕人的. 首先我们来分析一下题目,"小明可以使用 Cij个航 ...

  8. 2020 乐山师范学院新生程序设计大赛题解

    2020 乐山师范学院新生程序设计大赛题解 A 数组求和 中等难度 题目大意: 给定一个数组,执行若干次操作,每次操作可以把两个相邻的数变成相反数,求数组最大的和. 解题思路: 这其实是一个思维题,认 ...

  9. 金莹江苏省计算机学会教授,2020年江苏省大学生程序设计大赛在我校举办

    11月15日,2020年江苏省程序设计大赛在河海大学举办.河海大学副校长董增川教授.江苏省计算机学会秘书长金莹教授出席闭幕式并致辞. 河海大学副校长董增川教授在致辞中表示,河海大学不断深化教育教学改革 ...

最新文章

  1. IT人士在职场中需要哪些Skill?
  2. 解决Sqlite中的中文路径问题
  3. 缓存redis的使用方案
  4. Linux维护笔记五
  5. SecureCRT远程登录ubuntu
  6. 每周工作4天半可行吗?人社部回应:不宜在企业中广泛推行
  7. vue 指令 v-on 事件修饰符-鼠标事件-什么是事件冒泡
  8. 小学计算机第一册下教案,广东省小学信息技术第一册(下)全册教案
  9. 微型计算机控制是微机原理吗,微型计算机控制技术学习心得
  10. sas不能安装独立的java_SAS安装问题解决办法
  11. MATLAB一元三次求极值,MATLAB实现一元三次方程求解/盛金公式
  12. 我的心酸求职之路:如果可以,去学Java、C,不要搞Python
  13. 程序员:打死不找产品当女友,哪怕长得像天仙???丨黑马头条
  14. 【链块技术10期】区块链基础语言(二)——GO语言开发环境搭建
  15. 可爱的小老鼠计算机教案,大班语言儿歌教案《小老鼠玩电脑》
  16. 自我介绍自己对未来的期许
  17. 2017计算机一级教学进度表,2016--2017年度第二学期16职高一年级《计算机应用基础》教学进度表...
  18. 基于模板匹配和遗传算法的人眼定位
  19. 用 Java 实现贪吃蛇小游戏
  20. 为什么你996地辛苦工作,却没能升职加薪?因为你缺少3种至关重要的思维

热门文章

  1. c语言 编程 打怪,一起用C语言打怪物
  2. MyBatis遇到:There is no getter for property named ‘Xxx‘ in ‘class xxx.xxx.Xxx‘问题
  3. 根据word模板(书签)创建导出word
  4. 网页浏览器的发展详史
  5. 如何在期货与现货市场之间套利?
  6. python实现雪花飘落效果_python实现雪花飘落效果实例讲解及源码分享!
  7. foxmail发邮件时总提示接收密码错误是怎么回事
  8. One PUNCH Man——变量显著性检验
  9. 目标检测 YOLOv5 - ncnn模型的加密 C++实现封装库和Android调用库示例
  10. j2ee期末考试总结