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