[2018 NUIST 程序设计竞赛] P1553 抑郁的竹鼠

题目描述

一只竹鼠正处在由 N × M 个房间组成的矩阵迷宫中,它需要尽快逃出去,否则可能会变得抑郁,然后被农华兄弟吃掉。

为了描述方便,我们把左上角的房间的坐标定为 (0, 0),右下角房间的坐标定为 (N - 1, M - 1)。每个房间可能是 3
种状态之一:开放的、关闭的、或者上锁的。

开放房间用 ‘.’ 表示。竹鼠可以随意进入任何一个开放房间。

关闭房间用 ‘#’ 表示。竹鼠永远不能进入一个关闭的房间。

上锁的房间用大写字母 (‘A’, ‘B’, ‘C’, …)
表示。在取得相应的钥匙前不能进入上锁的房间,而一旦取得钥匙就可以反复进入上锁的房间。每个房间的锁都是不同的,相应的钥匙在迷宫中的某一房间里,竹鼠进入该房间就可以取得钥匙。一个房间最多只有一把锁,一个房间里最多只有一把钥匙。

竹鼠一开始处于一个开放房间,坐标 (a, b)。迷宫的出口是一个开放或者上锁的房间,坐标 (c,
d)。假设竹鼠每移动到一个相邻房间(上下左右)需要花费 1 单位的时间,那么这只竹鼠到达出口最少需要花费多少时间?

输入描述

题目包含多组测试数据。第一行包括一个正整数 T,代表测试数据的组数。

接下来的输入,对于每组测试数据:

第 1 行包含 7 个整数: N, M, K, a, b, c, d。其中 N, M 是矩阵的行列数;K 是上锁的房间数目,(a, b)
是起始位置,(c, d) 是出口位置。(1 ≤ N, M ≤ 100; 0 ≤ K ≤ 5; 0 ≤ a, c < N; 0 ≤ b, d
< M)

第 2 行到第 N + 1 行,每行包含 M 个字符,表示迷宫矩阵。

第 N + 2 行到第 N + K + 1 行,每行两个整数 x, y,依次表示上锁房间 A, B, C, … 的钥匙所在房间坐标。(0
≤ x < N, 0 ≤ y < M)

输出描述

对于每组输入,输出到达出口的最短时间。如果这只竹鼠永远到达不了出口,输出 -1。

样例输入

2
4 4 2 0 0 0 3
.A.B
.#…
.#…
.#…
3 0
3 3
5 5 3 0 0 0 3
.A.B.
.#…
.#…
.#…
…#.C
3 0
4 4
4 1

样例输出

15
23

思路

bfs模板题,难就难在搜索过程中的状态表示,我比较菜,就直接把vis[maxn][maxn]数组换成了vis[maxn][maxn][2][2][2][2][2],前两个是坐标,后五个是有没有A、B…对应的钥匙,取值0或1,然后就是套bfs的板子

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
const int maxn = 107;

char mp[maxn][maxn];
bool vis[maxn][maxn][2][2][2][2][2];
map<P, char> key_room;

int dx[]={
    
    1,0,-1,0};
int dy[]={
    
    0,1,0,-1};
int n, m, k, a, b, c, d;
struct node {
    
    
	int x, y, step;
	int key[5];
};
queue<node> q;
inline bool is_in(int x, int y) {
    
    
	return x>=0&&x<n&&y>=0&&y<m;
}
int bfs() {
    
    
	node t;
	t.x=a;
	t.y=b;
	t.step=0;
	fill(t.key, t.key+5, 0);

	for (int i=0;i<k;i++) {
    
    
		if (key_room[P(a, b)] >= 'A' && key_room[P(a, b)] <= 'E') {
    
    
			t.key[key_room[P(a, b)] - 'A'] = 1;
			break;
		}
	}
	vis[t.x][t.y][t.key[0]][t.key[1]][t.key[2]][t.key[3]][t.key[4]] = true;
	q.push(t);

	while (!q.empty()) {
    
    
		node tp = q.front();q.pop();

		for (int i=0;i<4;i++) {
    
    
			int nxtx=tp.x+dx[i], nxty=tp.y+dy[i];
			t = tp;
			if (!is_in(nxtx, nxty))  continue;//越界了
			if (mp[nxtx][nxty] == '#')  continue;//#不能走
			if (vis[nxtx][nxty][t.key[0]][t.key[1]][t.key[2]][t.key[3]][t.key[4]])  continue;//之前走过了
			if (mp[nxtx][nxty]>='A' && mp[nxtx][nxty]<='E' && tp.key[mp[nxtx][nxty] - 'A' ] == 0)  continue;//没钥匙

			if (key_room.count(P(nxtx, nxty))) {
    
    //拿钥匙
				t.key[key_room[P(nxtx, nxty)] - 'A' ] = 1;
			}

			if (nxtx == c && nxty == d) {
    
    //到终点了
				return tp.step+1;
			}
			
			t.x=nxtx;
			t.y=nxty;
			t.step=tp.step+1;
			vis[nxtx][nxty][t.key[0]][t.key[1]][t.key[2]][t.key[3]][t.key[4]]=true;
			q.push(t);

		}
	}
	return -1;
}
int main()
{
    
    
	

	int T, tx, ty;
	scanf("%d", &T);
	while (T--) {
    
    
		//清理数据
		memset(vis, 0, sizeof(vis));
		key_room.clear();
		while (!q.empty()) {
    
    q.pop();}

		scanf("%d %d %d %d %d %d %d", &n, &m, &k, &a, &b, &c, &d);
		for (int i=0;i<n;i++) {
    
    
			scanf("%s", mp[i]);
		}
		for (int i=0;i<k;i++) {
    
    
			scanf("%d %d", &tx, &ty);
			key_room[P(tx, ty)] = i + 'A';
		}
		printf("%d\n", bfs());

	}

	return 0;
}

ps:这题是18年南信大的校赛题,当年我才大一,连bfs都不会(虽然现在也很菜)

猜你喜欢

转载自blog.csdn.net/u010017231/article/details/106222769