您的位置:首页 > 其它

(HDU Tempter of the Bone II) BFS + 可捡炸弹炸墙的迷宫问题

2017-05-17 23:11 555 查看
Tempter of the Bone II

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 98304/32768 K (Java/Others)

Total Submission(s): 1090 Accepted Submission(s): 272

Problem Description

The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze was changed and the way he came in was lost.He realized that the bone was a trap, and he tried desperately to get out of this maze.

The maze was a rectangle with the sizes of N by M. The maze is made up of a door,many walls and many explosives. Doggie need to reach the door to escape from the tempter. In every second, he could move one block to one of the upper, lower, left or right neighboring blocks. And if the destination is a wall, Doggie need another second explode and a explosive to explode it if he had some explosives. Once he entered a block with explosives,he can take away all of the explosives. Can the poor doggie survive? Please help him.

Input

The input consists of multiple test cases. The first line of each test case contains two integers N, M,(2 <= N, M <= 8). which denote the sizes of the maze.The next N lines give the maze layout, with each line containing M characters. A character is one of the following:

‘X’: a block of wall;

‘S’: the start point of the doggie;

‘D’: the Door;

‘.’: an empty block;

‘1’–‘9’:explosives in that block.

Note,initially he had no explosives.

The input is terminated with two 0’s. This test case is not to be processed.

Output

For each test case, print the minimum time the doggie need to escape if the doggie can survive, or -1 otherwise.

Sample Input

4 4

SX..

XX..

….

1..D

4 4

S.X1

….

..XX

..XD

0 0

Sample Output

-1

9

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2128

这题出现在BFS专题里,是yobobobo提出做的,我便看了看,中途也和yobobobo讨论了许多,得到一些启发

这题网上好多的BFS代码都是错的,HDOJ的数据比较弱

题目意思不难理解,取一些炸弹,炸开一些墙以最快的速度找到出口。由于有最短时间的问题,首选就是BFS了,其实后来想想也许用DFS早就解决了。

如果用BFS的话状态表示以及判重会有很大问题,一开始觉得flag[x][y][bomb]判重就够了,其实不然,相同的bomb数量其实状态是不一样的,

后来想到由于会炸掉一些墙,会取走一些炸弹,就得传递整个图。一下子在结点里放放入两个二维数组,想了想地图不是很大,应该不 会MLE,

那么在flag[x][y][bomb]每个状态取最小的步数的情况,最终必然是WA了,因为步数越小不一定是最佳,还要牵扯整个图的情况。于是想到用整

张图来判重,vectorflag[x][y][bomb]来判重,结点里还有二维数组,YY了半天之后,提交果断是MLE,之前在和yobobobo讨论的时候

便想到了二进制压缩状态保存整个图,因为8*8刚好64位整数可以保存,而且不论是墙还是炸弹个数都少于64,果断改成二进制压缩,注意一些细节之后

测了许多数据没问题提交还是WA,不过果断发现是位运算溢出了,囧,再提交还是WA,一下子陷入悲剧当中。不断修改,各种错误都出来了,MLE

WA,TLE,都是由于状态判重部分导致。最后仔细一看题,发现是0 0结束,我却一直只是判断EOF,囧死,终于AC。

艰难的一题啊,不过yobobobo的探索精神令人感动,加油啊!!!

代码里有注释,另外附上一组数据

6 5

S.XX1

X.1X1

XX.X.

XXXXX

XXXXX

XXXDX

答案是17并不是-1

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define LL unsigned long long
using namespace std;
int n,m;
int way[4][2]={{0,1},{0,-1},{1,0},{-1,0}};   //BFS四个方向
char mmap[10][10];                           //原始地图
int wallcnt,wallmap[10][10];                 //原始地图上墙的数量,以及分布图,对于每个墙给个编号
int bombcnt,bombmap[10][10];                 //原始地图上炸弹的数量,以及分布图,对于每个炸弹给个编号
struct Node{
int x,y,step,bomb;      //位置,步数,剩余炸弹
LL vis;                 //地图上的炸弹已取情况,1表示某处炸弹已取
LL wall;                //地图上剩余墙的情况,1表示某处还有墙
bool check(){           //判断是否在地图范围内
if(x>=0&&y>=0&&x<n&&y<m)
return true;
return false;
}                       //优先队列
bool operator<(const Node n1) const{
return step>n1.step;
}
}s,e,u,v;
vector<Node>flag[8][8][8*8*9+1];   //判重,flag[x][y][z]表示 在(x,y)处手上有z个炸弹时的状态
bool check(Node tmp){              //判断是否和之前已入队的情况相同
for(int i=0;i<flag[tmp.x][tmp.y][tmp.bomb].size();i++)
if(tmp.wall==flag[tmp.x][tmp.y][tmp.bomb][i].wall&&tmp.vis==flag[tmp.x][tmp.y][tmp.bomb][i].vis)    //结点剩余的墙情况以及炸弹情况完全相同
return false;
return true;
}
int bfs(){
priority_queue<Node>que;
que.push(s);
while(!que.empty()){
u=que.top();
que.pop();
for(int i=0;i<4;i++){
v=u;
v.step++;
v.x=u.x+way[i][0];
v.y=u.y+way[i][1];
if(v.check()){
if(mmap[v.x][v.y]=='D')          //找到终点
return v.step;
if(mmap[v.x][v.y]=='X'&&((1LL<<wallmap[v.x][v.y])&v.wall)){    //某处原来是墙,而且也没被炸毁
if(v.bomb>0){    //手上有炸弹
v.bomb--;
v.step++;
v.wall^=(1LL<<wallmap[v.x][v.y]);    //标记下,已经被炸毁
que.push(v);                           //结点入队
flag[v.x][v.y][v.bomb].push_back(v);   //保存状态
}
}
else if(mmap[v.x][v.y]>='1'&&mmap[v.x][v.y]<='9'&&(v.vis&(1LL<<bombmap[v.x][v.y]))==0){    //某处是炸弹,而且之前没有取
v.bomb+=mmap[v.x][v.y]-'0';             //取炸弹
v.vis|=(1LL<<bombmap[v.x][v.y]);         //标记炸弹已取
que.push(v);                              //结点入队
flag[v.x][v.y][v.bomb].push_back(v);       //保存状态
}
else{
if(flag[v.x][v.y][v.bomb].empty()||check(v)){   //当前没有状态或者和之前的状态都不相同
flag[v.x][v.y][v.bomb].push_back(v);      //保存
que.push(v);         //入队
}
}
}

}
}
return -1;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&n+m){
for(int i=0;i<n;i++)           //判重初始化
for(int j=0;j<m;j++)
for(int k=0;k<=(n*m*9);k++)
if(!flag[i][j][k].empty())
flag[i][j][k].clear();
bombcnt=wallcnt=0;
memset(wallmap,-1,sizeof(wallmap));
memset(bombmap,-1,sizeof(bombmap));
for(int i=0;i<n;i++){
scanf("%s",mmap[i]);
for(int j=0;j<m;j++)
if(mmap[i][j]=='S'){            //起点标记
s.x=i;
s.y=j;
s.step=0;
s.bomb=0;
s.vis=0;
}
else if(mmap[i][j]=='X')
wallmap[i][j]=wallcnt++;             //对于每个墙给个编号
else if(mmap[i][j]>='1'&&mmap[i][j]<='9')
bombmap[i][j]=bombcnt++;           //对于每个炸弹位置给个编号
}
s.wall=(1LL<<wallcnt)-1;               //初始化,所有墙都没有被炸
printf("%d\n",bfs());
}
return 0;
}


转自 http://blog.csdn.net/ACM_cxlove?viewmode=contents by—cxlove
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: