一只小狗在一个古老的迷宫里发现了一块骨头,它太喜欢了。当它拿起来这个骨头的时候,迷宫开始坍塌。小狗意识到这是个陷阱,它拼了命想逃离。
迷宫有n行m列,其中X代表墙,小狗不能走入。S表示小狗起始位置,D代表出口,.表示空地。
小狗能往上下左右四个方向走,迷宫中有一道出口门,只在第T秒时开启,持续一秒。问小狗能否逃离。
小狗原位置到达新位置需要1秒,且原位置会坍塌(小狗不能往回走)
1<N,M<7,T<50
1000ms | 32768KB |
---|
BFS做法(空间超限)
这道题真的不建议用BFS来做
一般BFS要改原图,然后把新状态(x坐标,y坐标,经过时间)加入队列,但是下一个结点出来的时候(注意,BFS广搜,相邻的两个状态结点是并列关系,他们是从同一个状态开的多分支,也就是平行宇宙),还是会参照原图,所以原图就一直在被更新,而没有回溯。意思就是假如现在队列出队的是1,然后对于状态结点1又有四个方向可以入队,我把1所在的位置标记为X,1处理完了,然后出队的是2,状态2是从状态0过来的,但是由于先出队的1修改了原图,所以状态2参照的原图是不准确的,可以说是时空交错了。状态2的时间状态对应了错误的空间状态。
所以为了避免队列前面的结点修改原图对后面的结点造成影响,就不能只有一个原图,所以对于每个状态结点都保存自己的空间状态,这样每次出队结点都拿自己的空间状态来做处理,就不会有错,但是这样的空间花费是巨大的
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define DEBUG
using namespace std;
struct node{
int x, y, t,state;
node() {};
node(int x, int y, int t,int state){
this->x = x;
this->y = y;
this->t = t;
this->state = state;
};
};
int dir[4][2] =
{
{0,1},
{0,-1},
{1,0},
{-1,0}
};
char g[250000][7][7];
int n, m, t;
int sx, sy, ex, ey;
//int maxsize;
char temp;
int all_state;
bool bfs(int sx,int sy) {
queue<node> q;
q.push(node(sx, sy, 0, all_state));
node now;
int x, y, nx, ny;
while (q.size()) {
#ifdef DEBUG
//maxsize = max(maxsize, (int)q.size());
#endif // DEBUG
now = q.front();
q.pop();
#ifdef DEBUG
printf("x=%d y=%d t=%d state=%d\n", now.x, now.y, now.t,now.state);
printf("\n");
for (int i = 0; i < n; i++) {
printf("%s\n", g[now.state][i]);
}
printf("\n");
#endif // DEBUG
x = now.x;
y = now.y;
if (now.t == t) {
if (x == ex && y == ey) {
#ifdef DEBUG
for (int i = 0; i < n; i++) {
printf("%s\n", g[now.state][i]);
}
#endif // DEBUG
return true;
}
continue;
}
if(g[now.state][x][y]!='S') g[now.state][x][y] = '*';
for (int i = 0; i < 4; i++) {
nx = x + dir[i][0];
ny = y + dir[i][1];
temp = '0';
if (nx >= 0 && nx < n && ny >= 0 && ny < m && g[now.state][nx][ny] != 'X'&&g[now.state][nx][ny] != 'S' && g[now.state][nx][ny] != '*') {
swap(temp, g[now.state][nx][ny]);
memcpy(g[++all_state], g[now.state], sizeof g[now.state]);
q.push(node(nx, ny, now.t + 1,all_state));
swap(temp, g[now.state][nx][ny]);
}
}
}
return false;
}
int main() {
//printf("struct size:%d\n", sizeof node);
while (scanf("%d %d %d", &n, &m, &t), (n&&m&&t)) {
//maxsize = 0;
memset(g, 0, sizeof g);
all_state = 0;
for (int i = 0; i < n; i++) {
scanf("%s", g[all_state] + i);
for (int j = 0; j < m; j++) {
if (g[all_state][i][j] == 'S') {
sx = i;
sy = j;
}
if (g[all_state][i][j] == 'D') {
ex = i;
ey = j;
}
}
}
if ((ex + ey + sx + sy + t) % 2 != 0) {
printf("NO\n");
continue;
}
if (bfs(sx, sy)) {
printf("YES\n");
}
else {
printf("NO\n");
}
//printf("maxsize:%d\n", maxsize);
}
return 0;
}
运行样例 |
---|
6 6 12 S.X... ..X..X ..XX.. ...... ...... .....D
可以看到队列的容量达到了1673
运行样例 |
---|
6 6 27 S.X..D ..X..X ..XX.. ...... ..X... ......
队列达到了2522
我觉得我的算法是没有问题的,就是内存超了
经过OJ的多次打击之后,我还是走正道了
DFS做法
思路就是从起始点走呗,从这个点走到下个点,我就把这个点变成X,为了防止往回走,然后又是四个方向走下去,如果时间超了,就说明这种走法不行,得回到上一个点换个方向走,如果上一个点的所有方向都走完了还是不行,就得回溯改,把X换回 . ,给以后的走法留条路。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
//#define DEBUG
using namespace std;
int n, m, t;
char g[7][7];
int sx, sy, ex, ey;
int dir[4][2] = {
{0,1},
{0,-1},
{1,0},
{-1,0}
};
bool ok;
bool check(int x, int y) {
return x >= 0 && x < n&&y >= 0 && y < m&&g[x][y] != 'X';
}
void dfs(int x, int y, int time) {
#ifdef DEBUG
printf("x=%d y=%d time=%d\n", x, y, time);
for (int i = 0; i < n; i++) {
puts(g[i]);
}
printf("\n");
#endif // DEBUG
if (time == t) {
if (x == ex && y == ey) {
ok = true;
}
return;
}
int nx, ny;
g[x][y] = 'X';
for (int i = 0; i < 4; i++) {
nx = x + dir[i][0];
ny = y + dir[i][1];
if (check(nx, ny)) {
dfs(nx, ny,time+1);
if (ok)return;
}
}
g[x][y] = '.';
}
int main() {
while (scanf("%d %d %d", &n, &m, &t), (n&&m&&t)) {
ok = false;
memset(g, 0, sizeof g);
for (int i = 0; i < n; i++) {
scanf("%s", g + i);
for (int j = 0; j < m; j++) {
if (g[i][j] == 'S') {
sx = i;
sy = j;
}
if (g[i][j] == 'D') {
ex = i;
ey = j;
}
}
}
if ((ex + ey + sx + sy + t) % 2 != 0) {
printf("NO\n");
continue;
}
dfs(sx, sy,0);
if (ok) {
printf("YES\n");
}
else {
printf("NO\n");
}
}
return 0;
}
/*
6 6 27
S.X..D
..X..X
..XX..
......
..X...
......
*/
有些时候是这么回事,求最优解的问题,比如迷宫最短路,最少操作几次,最少耗费时间,我们本能反应就是BFS,而且这类题型往往数据量还比较大,用DFS的话要遍历整个解空间树求个最小值,就会TLE,而这道题目我们也本能地以为可以用BFS做,但是这道题有个特殊的条件是你不能往回走,BFS怎么回溯啊,拷贝地图作为每一种情况,一一对应吗?那么就会MLE,用DFS反而更快。
总结
两种做法都用到了奇偶性剪枝,其实还有个剪枝:
- 如果起始点坐标和终点坐标的曼哈顿距离都小于t,那么直接不行
谨以此篇,纪念此题 |
---|