题目概况
链接: https://nanti.jisuanke.com/t/T1257 难度: 普及/提高-(计蒜客评价普及T3)
题目分析
简化题目: 一张地图中,起点是S,终点是E,有禁区#、传送门$、宝石0-4,安全区 . 这些地方,求拿着K种宝石到达E的最短时间,到不了就输出“oop!” 涉及知识点: 广度优先搜索BFS、二进制状态压缩、位运算 解题思路: 通过BFS,逐情况讨论状态即可,状态便是 (tx, ty, tk, tdis) tx,ty 表示坐标,tk 表示当前宝石数,tdis 表示当前步数。但我们的宝石需要用二进制表示压缩空间(11111就是五种宝石的状态,1的数量就是宝石的种数),要不然–boom!代码实现细节较多,难度较大
代码要点拆解
一、数据准备
struct node {
int x, y, k, dis;
node(int _x, int _y, int _k, int _dis) {
x = _x;
y = _y;
k = _k;
dis = _dis;
}
};
const int MAXN = 205;
int T;
int t;
int R, C;
int K;
int vis[MAXN][MAXN][MAXN];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int door[15][15];
char maze[MAXN][MAXN];
queue <node> q;
二、主函数内的玄学 操作
要点一览: 1. 在使用前清空不能继续用的数据 2. 输入地图,同时记录起点、传送门 3. 开始BFS记录答案 然后再看注释理解。
int main() {
cin >> T;
while (T--) {
t = 0;
memset(vis, 0, sizeof(vis));
while (!q.empty()) {
q.pop();
}
int startx, starty;
cin >> R >> C >> K;
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
cin >> maze[i][j];
if (maze[i][j] == 'S') {
startx = i;
starty = j;
}
if (maze[i][j] == '$') {
door[t][0] = i;
door[t][1] = j;
t++;
}
}
}
int ans = bfs(startx, starty);
if (ans == -1) {
cout << "oop!" << endl;
} else {
cout << ans << endl;
}
}
return 0;
}
三、BFS中的特殊情况考虑
私以为的一个点可以让BFScontinue 或者直接return ,不需要继续求状态的就是特殊情况: 1. 不在范围内的 2. 已经访问过的 3. 该点是禁区的 4. 该点是终点的(记得判断一下到终点了宝石够不够)
if (!in(tx, ty)) {
continue;
}
if (vis[tx][ty][tk]) {
continue;
}
if (maze[tx][ty] == '#') {
continue;
}
if (maze[tx][ty] == 'E') {
if (check(tk)) {
return tdis + 1;
}
}
四、BFS中的一般情况的状态处理
1.安全区
安全区直接把步数加1即可
if (maze[tx][ty] == '.') {
vis[tx][ty][tk] = true;
q.push(node(tx, ty, tk, tdis + 1));
}
2.传送门
题面中说过:“当然蒜头君也可以选择不通过传送门瞬移”。 所以我们分情况考虑用传送门和不用传送门两种情况 细节问题见注释。
if (maze[tx][ty] == '$') {
vis[tx][ty][tk] = true;
q.push(node(tx, ty, tk, tdis + 1));
for (int j = 0; j < t; j++) {
if (!vis[door[j][0]][door[j][1]][tk]) {
vis[door[j][0]][door[j][1]][tk] = true;
q.push(node(door[j][0], door[j][1], tk, tdis + 1));
}
}
}
3.捡宝石(难点)
位运算相关:https://www.runoob.com/w3cnote/bit-operation.html tk = tk|(1 << x) 表示向tk的第x位赋值1
if (maze[tx][ty] >= '0' && maze[tx][ty] <= '9') {
vis[tx][ty][tk] = true;
tk = tk|(1 << (maze[tx][ty] - '0'));
q.push(node(tx, ty, tk, tdis + 1));
}
五、宝石检查函数(难点)
(x >> i) & 1 表示判断x 的第i 位是否为1
bool check(int x) {
int l = 0;
for (int i = 0; i < 5; i++) {
if ((x >> i)&1) {
l++;
}
}
if (l >= K) {
return true;
}
return false;
}
最后还有个in函数,用来判断是否在地图范围内的,我想在座各位应该都会,就不写了
完整代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
struct node {
int x, y, k, dis;
node(int _x, int _y, int _k, int _dis) {
x = _x;
y = _y;
k = _k;
dis = _dis;
}
};
const int MAXN = 205;
int T;
int t;
int R, C;
int K;
int vis[MAXN][MAXN][MAXN];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int door[15][15];
char maze[MAXN][MAXN];
queue <node> q;
bool in(int a, int b) {
return a >= 1 && a <= R && b >= 1&& b <= C;
}
bool check(int x) {
int l = 0;
for (int i = 0; i < 5; i++) {
if ((x >> i)&1) {
l++;
}
}
if (l >= K) {
return true;
}
return false;
}
int bfs(int xx, int yy) {
node now(xx, yy, 0, 0);
q.push(now);
vis[xx][yy][0] = true;
while (!q.empty()) {
now = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int tx = now.x + dir[i][0];
int ty = now.y + dir[i][1];
int tk = now.k;
int tdis = now.dis;
if (!in(tx, ty)) {
continue;
}
if (vis[tx][ty][tk]) {
continue;
}
if (maze[tx][ty] == '#') {
continue;
}
if (maze[tx][ty] == 'E') {
if (check(tk)) {
return tdis + 1;
}
}
if (maze[tx][ty] == '.') {
vis[tx][ty][tk] = true;
q.push(node(tx, ty, tk, tdis + 1));
}
if (maze[tx][ty] == '$') {
vis[tx][ty][tk] = true;
q.push(node(tx, ty, tk, tdis + 1));
for (int j = 0; j < t; j++) {
if (!vis[door[j][0]][door[j][1]][tk]) {
vis[door[j][0]][door[j][1]][tk] = true;
q.push(node(door[j][0], door[j][1], tk, tdis + 1));
}
}
}
if (maze[tx][ty] >= '0' && maze[tx][ty] <= '9') {
vis[tx][ty][tk] = true;
tk = tk|(1 << (maze[tx][ty] - '0'));
q.push(node(tx, ty, tk, tdis + 1));
}
}
}
return -1;
}
int main() {
cin >> T;
while (T--) {
t = 0;
memset(vis, 0, sizeof(vis));
while (!q.empty()) {
q.pop();
}
int startx, starty;
cin >> R >> C >> K;
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
cin >> maze[i][j];
if (maze[i][j] == 'S') {
startx = i;
starty = j;
}
if (maze[i][j] == '$') {
door[t][0] = i;
door[t][1] = j;
t++;
}
}
}
int ans = bfs(startx, starty);
if (ans == -1) {
cout << "oop!" << endl;
} else {
cout << ans << endl;
}
}
return 0;
}
|