随机传送迷宫
发布时间: 2017年7月9日 18:17 最后更新: 2017年7月9日 21:04 时间限制: 1000ms 内存限制: 128M
小Y做了一个随机迷宫生成器,生成的迷宫可以用
n∗m
的矩阵来表示,人物可以从迷宫的起点开始,每个单位时间选择往上下左右走一步,但不能走到不能通行的格子或者走出矩阵外。在迷宫中会有不定数量的传送门,其中传送门入口与传送门出口一一对应,如果人物处在传送门入口处,可以用一个单位的时间使用这个传送门入口到达一个传送门出口处,但是在尝试之前你并不知道哪一个传送门入口对应哪一个出口,因此小Y想知道用什么样的策略走才能在最坏情况下花费最少时间到达迷宫的出口。传送门只能单向通行,但是可以重复使用,由于传送门入口出口一一对应,因此重复使用传送门会到达同一个传送门出口。
第一行有一个整数
T
表示数据组数。(
T<=50
)
接着有
T
组数据,每组数据第一行有两个整数
n
、
m
。(
2<=n
,
m<=100
)
接着有
n
行,
m
列的迷宫矩阵。
其中字符'S','T'分别表示唯一的迷宫起点和终点,
字符'#'表示无法通行的障碍,
字符'.'表示空地,
字符'i','o'分别表示传送门入口和出口。(传送门入口与出口数量相同,并且数量都不超过5)
对于每组数据输出一行,表示花费的最少单位时间,如果不存在一定能走到出口的方案,则输出-1。
3 4 4 Siii ..oo #### T..o 4 4 S..i i### #.#o T.o# 5 4 o.Si ###i .oo# i### i#oT
9 -1 16
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 105, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m, g;
char s[N][N];
struct Point
{
int y, x;
}ST, ED;
//用于求出状态,得到ID->确定状态的映射match,和确定状态->ID的映射mop
vector<int> VT;
vector<int> match[2000]; int matchID;
map<vector<int>, int> mop;
bool vis[10];
void dfs(int p)
{
if (p == g)
{
match[matchID] = VT;
mop[VT] = matchID++;
return;
}
VT[p] = g;
dfs(p + 1);
for (int i = 0; i < g; ++i)if (!vis[i])
{
vis[i] = 1;
VT[p] = i;
dfs(p + 1);
vis[i] = 0;
}
}
//用于求出特殊点之间的距离
Point I[10], O[10];
int in[N][N], out[N][N];
const int dy[4] = { -1, 0, 0, 1 };
const int dx[4] = { 0, -1, 1, 0 };
int DIS[6][6];
int d[N][N];
void bfs(int o)
{
MS(d, 63); d[O[o].y][O[o].x] = 0;
queue<Point>q; q.push(O[o]);
while (!q.empty())
{
Point now = q.front(); q.pop();
int step = d[now.y][now.x] + 1;
for (int k = 0; k < 4; ++k)
{
int yy = now.y + dy[k];
int xx = now.x + dx[k];
if (s[yy][xx] == '#')continue;
if (step < d[yy][xx])
{
d[yy][xx] = step;
q.push({ yy,xx });
if (in[yy][xx] <= g)
{
DIS[o][in[yy][xx]] = step;
}
}
}
}
}
//状态由两部分构成,当前的状态,最后的位置
int f[2000][6];
int canIN[6];
int canOUT[6];
bool sureOUT[6];
int dfs(int ID, int p)
{
if (f[ID][p] != inf)return f[ID][p];
int canINnum = 0, canOUTnum = 0;
MS(sureOUT, 0);
for (int i = 0; i < g; ++i)
{
if (match[ID][i] == g)
canIN[canINnum++] = i;
else
sureOUT[match[ID][i]] = 1;
}
for (int i = 0; i < g; ++i)
if (!sureOUT[i])
canOUT[canOUTnum++] = i;
//枚举入口
f[ID][p] = DIS[p][g];
for (int i = 0; i < canINnum; ++i)
{
int gohere = 0;
int x = canIN[i];
//枚举出口
for (int j = 0; j < canOUTnum; ++j)
{
int y = canOUT[j];
if (DIS[p][x] == inf)
{
gohere = inf;
break;
}
vector<int>vt = match[ID];
vt[x] = y;
int nxtID = mop[vt];
gmax(gohere, DIS[p][x] + 1 + dfs(nxtID, y));
}
gmin(f[ID][p], gohere);
}
return f[ID][p];
}
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d%d", &n, &m);
for (int i = 0; i <= n + 1; ++i)
{
for (int j = 0; j <= m + 1; ++j)
{
s[i][j] = '#';
in[i][j] = out[i][j] = inf;
}
}
int ig = 0, og = 0;
for (int i = 1; i <= n; ++i)
{
scanf("%s", s[i] + 1); s[i][m + 1] = '#';
for (int j = 1; j <= m; ++j)
{
if (s[i][j] == 'S')
{
ST = { i,j };
}
else if (s[i][j] == 'T')
{
ED = { i,j };
}
else if (s[i][j] == 'i')
{
in[i][j] = ig;
I[ig++] = { i,j };
}
else if (s[i][j] == 'o')
{
out[i][j] = ig;
O[og++] = { i,j };
}
}
}
g = ig; O[g] = ST; I[g] = ED; in[ED.y][ED.x] = g;
MS(DIS, 63);
for (int i = 0; i <= g; ++i)
bfs(i);
mop.clear();
VT.resize(g);
matchID = 0;
dfs(0);
MS(f, 63);
int ans = dfs(0, g);
if (ans >= inf)ans = -1;
printf("%d\n", ans);
}
return 0;
}
/*
【trick&&吐槽】
【题意】
【分析】
【时间复杂度&&优化】
*/