保存现场的BFS-hdu-2128-Tempter of the Bone II

来源:互联网 发布:ibm云计算产品 编辑:程序博客网 时间:2024/06/05 21:49
题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2128

题目意思:

在一个矩阵中,给一个起点一个终点,矩阵中有的位置有墙,有的位置有炸弹,对于墙必须拿炸弹炸才能过,每走一步花费1s,每炸一次门花1s,问从起点到终点的最短时间。

解题思路:

BFS。(DFS会超时)

因为地图会变,所以普通的BFS,不行。必须要保存现场,使得之前不能到达的路径对地图的改变能够还原,使得对后面的正确路径无影响。

又对于矩阵任意位置,要么可走(1),要么不可走(0)。所以可以用二进制来保存地图。对每一个点保存多个状态地图,map<ull,int>myp[70]   myp[i][j]表示到达第i个点时,当前地图状态为j时的最小的步数。

对于某点某状态,如果之前已经访问了该点该地图状态下并且此时的步数更大,则不用访问此点。

否则还需访问。

用优先队列使得每次从步数最小的点开始访问。

对于判重状态,要深思熟虑,是否真正可行。

代码:

#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>#include<string>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#define eps 1e-6#define INF 0x1f1f1f1f#define PI acos(-1.0)#define ll __int64#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define  ull unsigned long long//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;/*freopen("data.in","r",stdin);freopen("data.out","w",stdout);*/struct Node{   ull s;   int bt,st,pos;   friend bool operator <(Node a,Node b)   {      return a.st>b.st;   }}ss;map<ull,int>myp[70]; //map[i][j]表示当前走到第i个格子,int n,m;                     //地图状态为j时,所用的最小时间int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};char save[70];priority_queue<Node>myq;void ispush(ull state,int pp,Node cur,int add,int a){   map<ull,int>::iterator it;   it=myp[pp].find(state); //   if(it!=myp[pp].end()&&cur.st+1>=(it->second)) //之前队列以访问了该点该状态且时间更少      return;                                    //故退出,相当于判重剪枝,记忆化搜索   Node tmp;   //对于炸弹、墙 要不同考虑   tmp.bt=cur.bt+a,tmp.pos=pp,tmp.s=state,tmp.st=cur.st+add;   myp[pp][tmp.s]=tmp.st;   myq.push(tmp);}int bfs(){   myq.push(ss);   myp[ss.pos][ss.s]=0;   while(!myq.empty())   {      Node cur=myq.top();      myq.pop();      int x=cur.pos/m,y=cur.pos%m; //位置坐标      int xx,yy;      for(int i=0;i<4;i++)      {         xx=x+dir[i][0],yy=y+dir[i][1]; //走一步         if(xx<0||xx>=n||yy<0||yy>=m) //走出去了,不行            continue;         int t=xx*m+yy;         if(save[t]=='D') //走到了跳出            return cur.st+1;         if(cur.s&((ull)1<<t)) //可以走            ispush(cur.s,t,cur,1,0);         else if(save[t]=='X') //有墙         {            if(cur.bt)            {               ull tt=cur.s|((ull)1<<t); //强炸掉后就可以走               ispush(tt,t,cur,2,-1);            }         }         else         {            ull tt=cur.s|((ull)1<<t); //炸弹拿了后可以走            ispush(tt,t,cur,1,save[t]-'0');         }      }   }   return -1;}int main(){   while(scanf("%d%d",&n,&m)&&n+m)   {      ss.s=0;      getchar();      for(int i=0;i<n;i++)      {         for(int j=0;j<m;j++)         {            int t=i*m+j;            scanf("%c",&save[t]);            if(save[t]=='S') //可以走               ss.pos=t,ss.bt=0,ss.st=0,ss.s|=((ull)1<<t);            else if(save[t]=='D'||save[t]=='.')               ss.s|=((ull)1<<t); //可以走         }         getchar();      }      for(int i=0;i<n*m;i++) //初始化         myp[i].clear();      while(!myq.empty())         myq.pop();      int ans=bfs();      printf("%d\n",ans);   }   return 0;}


附上几组测试数据:

8 8
......XD
.XXXX..X
.XXXXX..
.XXXXXXX
.XXXXXXX
...4XXXX
XXXXXXX3
XXXXXXXS

38

 

6 5
S.XX1
X.1X1
XX.X.
XXXXX
XXXXX
XXXDX

17

 

6 6
S1XX3X
XXXXXX
2XXXXX
XXXXXX
XXXXXX
X1XDXX
29

 

5 6
S.XXXX
21..XX
XXXXXX
XXXXXX
3XXXDX

11

 

5 3
S..
1X.
XX.
...
XXD

6