POJ 3346 Treasure of theChimp Island(BFS)
http://poj.org/problem?id=3346
题意:
有一个迷宫,有宝藏,你必须从迷宫边缘进去,迷宫边缘的门有可能让你携带0-9颗炸弹,在迷宫里面有’.’表示空地,你走过空地不需要花任何时间,有’*’表示不可行格子,你就算用炸弹也不能走到这种格子上.还有标记了数字的格子,这种格子如果你消耗一颗炸弹可以立即通过不花时间,你也可以花数字上的天数(1-9)天来打开障碍.问你从边缘到达宝藏的最短时间,如果宝藏不可到达则输出” IMPOSSIBLE”
分析:
首先我们可以想到以坐标+当前携带的炸弹数目作为一个状态.那么我们从迷宫的边缘入口依次添加初始状态到队列Q中.
然后我们开始从Q中取出每个状态并且向4个方向扩展,每个方向如果是’.’就直接走即可,如果是数字就要考虑是炸开还是花时间打开了.如果是炸开,要注意你要判断的是炸开后炸弹数-1的那个状态时候之前已经存在.注意:由于用了nTNT炸弹数作为状态描述的一部分,所以你可以刚炸开一个墙然后往前走了一步之后,又可以回头走到墙上,此时需要再炸一次(其实这就是炸弹走了回头路),不过没关系,就算走了回头路,也就是多了一些状态最终还是会收敛的.
如果你是花时间打开墙的话就不会走回头路,check函数会帮你把这个回头路过滤掉.
如果下一个格子是’#’,那就不能走.
如果下一个格子是’$’,那么就用当前time更新ans.且不用计算后继状态了,没有意义.(剪枝)
(其实如果某个节点所花的时间time已经==当前最优解ans了,直接可以剪枝,不用加到队列中去了)
AC代码:
#include<cstdio>
#include<cstring>
#include<ctype.h>
using namespace std;
const int maxn=1000000+3;
const int maxr=110;//如果开105就是 runtimeerror
const int maxc=110;
int R,C;
int dr[]={-1,1,0,0};//上,下,左,右
int dc[]={0,0,-1,1};
struct node
{
int r,c,nTNT;
int time;
node(){}
node(int r,int c,int nTNT,int time):r(r),c(c),nTNT(nTNT),time(time){}
}Q[maxn];//队列
int state[maxr][maxc][27];//记录到达该状态的最小时间
bool check(int r,int c,int nTNT,int t)
{
return (state[r][c][nTNT]==-1 || state[r][c][nTNT]>t);
}
char map[maxr][maxc];//保存地图信息
int BFS()
{
int front=0,tail=0;
int ans=1000000;
memset(state,-1,sizeof(state));
for(int i=0;i<R;i++) //边缘入口进队列
for(int j=0;j<C;j++)if(isupper(map[i][j]) || map[i][j]=='#')
{
Q[tail].r=i;
Q[tail].c=j;
Q[tail].nTNT=map[i][j]=='#'?0:map[i][j]-'A'+1;
Q[tail].time=0;
state[i][j][Q[tail].nTNT]=0; //错误1 这里漏了
tail++;
}
while(front<tail)
{
for(int d=0;d<4;d++)
{
int nr=Q[front].r+dr[d];
int nc=Q[front].c+dc[d];
int nTNT=Q[front].nTNT;
int time=Q[front].time;
if(nr>=1&&nr<=R-1&&nc>=1&&nc<=C-1&&map[nr][nc]!='*')
{
if(map[nr][nc]=='.' && check(nr,nc,nTNT,time))
{
state[nr][nc][nTNT]=time;
Q[tail]=node(nr,nc,nTNT,time);
tail++;//也可以这么写进行剪枝 if(que[tail].time<ans) tail++;
}
else if(isdigit(map[nr][nc]))//数字墙
{
if(nTNT>0 && check(nr,nc,nTNT-1,time))//炸开
{
state[nr][nc][nTNT-1]=time;
Q[tail]=node(nr,nc,nTNT-1,time);
tail++;//也可以这么写进行剪枝 if(que[tail].time<ans) tail++;
}
if(check(nr,nc,nTNT,time+map[nr][nc]-'0'))//花时间打开
{
state[nr][nc][nTNT]=time+map[nr][nc]-'0';
Q[tail]=node(nr,nc,nTNT,time+map[nr][nc]-'0');
tail++;//也可以这么写进行剪枝 if(que[tail].time<ans) tail++;
}
}
else if(map[nr][nc]=='$')
{
if(ans>time) ans=time;
}
}
}
front++;
}
return ans;
}
int main()
{
while(true)
{
R=C=0; //错误2,这句话之前写在了while(gets(map[0])) 的上面
while(gets(map[R]))
{
if(strlen(map[R]) == 0 ) break;
if(map[R][0]=='-') return 0;
R++;
}
C=strlen(map[0]);
int ans=BFS();
if(ans==1000000) printf("IMPOSSIBLE\n");
else printf("%d\n",ans);
}
return 0;
}