POJ 3346 Treasure of theChimp Island(BFS)

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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值