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

本文解析了HDU 2128题目的解题思路,采用BFS算法解决矩阵中起点到终点的最短路径问题,特别针对地图变化情况进行了详细说明,并提供了完整的代码实现及测试数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

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



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值