hdu~5025(bfs+状态压缩)

题目链接

题意如下:

悟空(K)救唐僧(T),在一个迷宫里,集齐m把钥匙方可救出唐僧(否则就算找到唐僧也只能和他擦肩而过0,0),条件

1.每次移动花费1个单位时间,遇到蛇(最多5条),杀死它花费一个单位时间。

2.如果你进入了一个房间放着第w把钥匙,只有在你有前w-1把钥匙的情况下你才能得到这把钥匙,否则不能得到。

比如 KT21 ,从K开始走 :右右右左左 花费5个单位时间。

3.输入 0   0表示结束。


       首先因为因为钥匙的问题想道bfs+状态压缩(这时想到的是压缩钥匙情况),


后面发现有蛇的存在没法bfs,换dfs+状态压缩(依然压钥匙)果断超时/(ㄒoㄒ)/~~,


再后面才发现蛇只有5条,所以蛇也可以压缩,但是以为是9把钥匙的排列(2^9),

钥匙那一维开了2100爆了,仔细一看题发现只要有第m把钥匙就肯定有前m-1把钥匙,

不可能出现101(即有第一三把 ,没有第二把)的情况。

(和以前做的 钥匙门 的状态压缩不一样O__O "…)

即钥匙数从1~m按序号慢慢叠加(具体请看代码)。


注意本人代码 存蛇的状态是从2^1开始存,

即000010代表第一条蛇被杀,111110表示5条蛇都被杀。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#define INF 0xfffffff

using namespace std;
typedef struct ac
{
    int x,y,t;
    int k,snak; //钥匙状态,蛇状态
}node;

int fmin(int a,int b){return a<b?a:b;}  
int dirx[]={0,0,1,0,-1},diry[]={0,1,0,-1,0};    
int snakx[10],snaky[10],snakl; //记录蛇的位置即序号
int m,n,time;
bool mark[105][105][10][64]; //判断在有不同的key的各个位置是否出现过
char maze[105][105];

int which_snak(int x,int y){  //判断该位置是第几条蛇
    for(int i=1;i<=snakl;++i)
        if(x==snakx[i] && y==snaky[i]) return i;
}

void bfs(node s){
    queue <node> q;
    mark[s.x][s.y][s.k][s.snak]=true;
    q.push(s);
    while(!q.empty()){
        s=q.front();
        q.pop();
       // printf("%d %d %d %d\n",s.x,s.y,s.k,s.snak);
        if(s.t>time)continue;   
        //凑齐m把钥匙就可以去取经了
        if(maze[s.x][s.y]=='T' && s.k==m){ 
            time =fmin(time,s.t);
            continue ;
        }
        for(int i=1;i<=4;i++){
            node w=s;
            w.x+=dirx[i];
            w.y+=diry[i];
            w.t++;
            //撞墙
            if(w.x<1 || w.x>n || w.y<1 || w.y>n 
               || maze[w.x][w.y]=='#') continue;            
            char c=maze[w.x][w.y];
            if(c=='S'){    //蛇
                int j=which_snak(w.x,w.y);
                if((w.snak&(1<<j))==0){//该蛇未死,
                    w.snak |= (1<<j);   
                    w.t++;          //杀掉(花费额外1个单位时间)
                  //  printf("!!\n");
                }
                if(!mark[w.x][w.y][w.k][w.snak]){   
                    q.push(w);
                    mark[w.x][w.y][w.k][w.snak]=true;
                }
            }
            else if(isdigit(c)){  //钥匙
                 //只有手上有前k把钥匙才能捡起第k+1把钥匙
                if(w.k+1==c-'0') w.k++;
                if(!mark[w.x][w.y][w.k][w.snak]){
                    mark[w.x][w.y][w.k][w.snak]=true;
                    q.push(w);
                }

            }
            else if(!mark[w.x][w.y][w.k][w.snak]){ //普通的路
                mark[w.x][w.y][w.k][w.snak]=true;
                q.push(w);
            }
        }
    }
}
int main()
{
    while(scanf("%d %d",&n,&m) && (m || n)){
        node s;
        memset(mark,false,sizeof(mark));
        snakl=0;
        for(int i=1;i<=n;i++){
            scanf("%s",&maze[i][1]);
            for(int j=1;j<=n;j++)
                if(maze[i][j]=='K') s.x=i,s.y=j;
                else if(maze[i][j]=='S') snakx[++snakl]=i,snaky[snakl]=j;
        }
        time=INF;
        s.k=0;
        s.snak=0;
        s.t=0;
        bfs(s);
        if(time==INF )printf("impossible\n");
        else printf("%d\n",time);
    }
    return 0;
}











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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值