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
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.
'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.
SX..
XX..
....
1..D
4 4
S.X1
....
..XX
..XD
0 0
9
分析:由题目可以得到这样一个状态:在某点(x,y)含有炸弹数num且炸毁过哪些墙(实际上我是错了几次才完事这个状态的)
如这组数据:
6 5
S.XX1
X.1X1
XX.X.
XXXXX
XXXXX
XXXDX
在(1,4)这个点含有炸弹数量为1的状态就有2种:1是炸墙(1,3)过去的,2事炸墙(2,3)过去的,不同的状态会导致不同的结果
所以用:vector<long long int>mark[MAX][MAX][MAX*MAX*9];//在i,j含有炸弹k时所炸过的墙,来记录,至于炸过的墙和拿过的炸弹(拿过了就不能再拿了,所以也要记录),在这里我用状态压缩来记录,用数的二进制表示中德0,1来表示否还是是,由于8*8的网格,恰好unsigned long long int 能表示
这题就是分析某点的状态和记录比较麻烦,其他都和一般的搜索一样
给几组数据:
6 5
S.XX1
X.1X1
XX.X.
XXXXX
XXXXX
XXXDX
2 6
S.1XXD
1..XXX
4 4
S1X1
XXXX
XXDX
XXXX
6 2
S1
..
1X
XX
XX
DX
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<vector>
#include<iomanip>
#define INF 99999999
using namespace std; const int MAX=8+10;
vector<long long int>mark[MAX][MAX][MAX*MAX*9];//在i,j含有炸弹k时所炸过的墙
char Map[MAX][MAX];
int n,m;
int dir[4][2]={0,1,0,-1,1,0,-1,0}; struct Node{
int x,y,num,time;
unsigned long long open;//表示已经炸过的墙(最多8*8位)
unsigned long long key;//表示已经取过的炸药位置(最多8*8)
Node(){}
Node(int X,int Y,int Num,int Time,unsigned long long Open,unsigned long long Key){
x=X,y=Y,num=Num;
time=Time,open=Open,key=Key;
}
bool operator<(Node const &a)const{
return time>a.time;
}
}start; bool check(Node &next){
int size=mark[next.x][next.y][next.num].size();
for(int i=0;i<size;++i){//判断在该位置拥有炸弹num的情况下炸过的墙是否一样
if(next.open == mark[next.x][next.y][next.num][i])return true;
}
return false;
} int BFS(){
priority_queue<Node>q;
Node oq,next;
q.push(start);
while(!q.empty()){
oq=q.top();
q.pop();
for(int i=0;i<4;++i){
next=Node(oq.x+dir[i][0],oq.y+dir[i][1],oq.num,oq.time+1,oq.open,oq.key);
if(next.x<0 || next.y<0 || next.x>=n || next.y>=m)continue;
if(Map[next.x][next.y] == 'X'){//该点是墙
int k=next.x*m+next.y;
if( !((next.open>>k)&1) )--next.num,++next.time;//是否已炸过
next.open|=((1ll)<<k);
}
if(Map[next.x][next.y]>='1' && Map[next.x][next.y]<='9'){//该点有炸药可取
int k=next.x*m+next.y;
if( !((next.key>>k)&1) )next.num+=Map[next.x][next.y]-'0';//是否已取过
next.key|=((1ll)<<k);
}
if(next.num<0 || check(next))continue;
mark[next.x][next.y][next.num].push_back(next.open);
if(Map[next.x][next.y] == 'D')return next.time;
q.push(next);
}
}
return -1;
} void Init(){
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
for(int k=0;k<=m*n*9;++k){
mark[i][j][k].clear();//初始化没炸过任何墙
}
}
}
} int main(){
while(cin>>n>>m,n+m){
Init();
memset(mark,-1,sizeof mark);
for(int i=0;i<n;++i)cin>>Map[i];
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(Map[i][j] == 'S')start=Node(i,j,0,0,0,0);
}
}
cout<<BFS()<<endl;
}
return 0;
}