HDU 1254 推箱子(双重BFS)

本文详细解析了一种推箱子游戏的算法实现,通过两次BFS(宽度优先搜索)来判断玩家能否将箱子推到目标位置。文章介绍了如何处理地图状态、判断玩家与箱子的相对位置及移动合法性,并提供完整代码示例。

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


不错的搜索题 ^^


分析:需要判断人和箱子的相对位置,两个bfs嵌套,挪动一次箱子就判断一次人是否能到达使箱子挪动的地方。两次bfs,因为数据量不大,所以应该可以过

很棒的搜索题

需要注意的地方:
   1.人不能穿过箱子,所以每次在处理人的行进路径时需要把地图G中箱子处转换成1
   2.箱子不一定沿着到达目标点的最短路径前进
   3.需要开4维数组记录人和箱子的相对状态state,因为人在箱子4个方向是不同的状态会产生不同的后继状态,因此需要标记

代码:

/* 
    Note: 
        一开始想把问题分解成两个子问题 
            1.用bfs找到最短路记录下路径 2.dfs判断人能否到达路径中每个点的前一个状态(即合法推箱子的状态)  
            后来发现有严重问题 
            例如: 
                1 
                7 6 
                4 1 1 1 1 1 
                0 1 0 0 0 1 
                0 1 0 1 0 1 
                0 2 0 1 0 3 
                0 0 0 1 0 1 
                0 0 0 0 0 1 
                0 0 0 0 0 1  
            我的思路只能选择两条最优路径中一条,然后进行判断,如果选择上面一条,那肯定是不对的,最合理的方法是选择下面的路径  
            所以这个思路是必错无疑 
         
        挪动一次箱子就判断一次人是否能到达使箱子挪动的地方 
        两次bfs,因为数据量不大,所以应该可以过        
*/  
#include<cstdio>    
#include<cstring>    
#include<queue>   
using namespace std;    
//#define test  
  
int n,m;    
int G[10][10];    
int vis[10][10];    
int state[9][9][9][9];          // 记录箱子和人的相对状态   
int box_x,box_y,per_x,per_y,x_e,y_e;    // box person X 的位置   
const int dx[]={-1,1,0,0};  
const int dy[]={0,0,-1,1};   
    
struct point{  
    int x,y;    
};    
struct node{                // 存放现在的步数step 方向r box & per 的位置   
    int r,step;    
    struct point box;    
    struct point per;    
};    
    
bool check_per(int x,int y){            // 判断person的行走是否合法    
    if(x<0 || x>=n || y<0 || y>=m || vis[x][y] )  return false;    
    return true;    
}    
bool judge_per(int x_s,int y_s,int x_e,int y_e){   // person能否从(x_s,y_s)到达(x_e,y_e)   
    queue<point>q;    
    point cur,next;      
  
    for(int i=0;i<n;i++)  
        for(int j=0;j<m;j++)    
            vis[i][j]=G[i][j];    
        
    cur.x=x_s;      cur.y=y_s;    
    vis[cur.x][cur.y]=1;    
    q.push(cur);    
    
    while(!q.empty()){    
        cur=q.front();    
        q.pop();          
        if(cur.x==x_e && cur.y==y_e)    return true;  
        for(int i=0;i<4;i++){    
            next.x=cur.x+dx[i];  
            next.y=cur.y+dy[i];    
            if(check_per(next.x,next.y)){  
                vis[next.x][next.y]=1;    
                q.push(next);   
            }  
        }    
    }    
    return false;    
}    
    
bool check_box(int x,int y){    
    if(x<0 || x>=n || y<0 || y>=m || G[x][y])  return false;    
    return true;  
}    
int judge_box(){    
    queue<node> q;    
    node cur,next;  
        
    cur.box.x=box_x;  cur.box.y=box_y;    
    cur.per.x=per_x;  cur.per.y=per_y;    
    cur.step=0;   
    for(int i=0;i<4;i++)     // 找到开始时人和箱子的相对方向r   
        if(cur.box.x-cur.per.x==dx[i] && cur.box.y-cur.per.y==dy[i]){    
            cur.r=i;  break;    
        }  
    state[cur.per.x][cur.per.y][cur.box.x][cur.box.y]=1;    
    q.push(cur);  
      
    while(!q.empty()){    
        cur=q.front();    q.pop();    
        if(cur.box.x==x_e && cur.box.y==y_e)    return cur.step;    
        for(int i=0;i<4;i++){    
            bool ok=false;  
            if(cur.r==i)    ok=true;    // 与人和箱子的相对方向相同时,开始推箱子   
            else{                       // 换个方向尝试推箱子   
                G[cur.box.x][cur.box.y]=1;    
                ok=judge_per(cur.per.x,cur.per.y, cur.box.x-dx[i],cur.box.y-dy[i]);    
                G[cur.box.x][cur.box.y]=0;    
            }    
            if(ok){  
                next.box.x=cur.box.x+dx[i];    next.box.y=cur.box.y+dy[i];    
                if( check_box(next.box.x,next.box.y) ){  // 检查箱子的移动是否合法   
                    next.per.x=cur.box.x;   next.per.y=cur.box.y;    
                    if(!state[next.per.x][next.per.y][next.box.x][next.box.y]){  
                        state[next.per.x][next.per.y][next.box.x][next.box.y]=1;    
                        next.r=i;       // 记录下人和箱子的相对方向   
                        next.step=cur.step+1;    
                        q.push(next);    
                    }  
                }  
            }  
        }    
    }    
    return -1;    
}    
  
int main(){    
    #ifdef test  
        freopen("Hdu 1254 推箱子 test case.txt","r",stdin);  
    #endif  
    int T;  
    int aim_x,aim_y;    
    scanf("%d",&T);    
    while(T--){    
        scanf("%d%d",&n,&m);    
        for(int i=0;i<n;i++)    
            for(int j=0;j<m;j++){    
                scanf("%d",&G[i][j]);    
                if(G[i][j]==2)        {box_x=i;box_y=j;   G[i][j]=0;}    
                else if(G[i][j]==3)   {x_e=i  ;y_e=j;     G[i][j]=0;}    
                else if(G[i][j]==4)   {per_x=i;per_y=j;   G[i][j]=0;}    
            }    
        // 一开始时人就无法到达箱子的周围的某一个格子,这时就不需要搜索了,直接-1   
        bool ok=false;    
        for(int i=0;i<4;i++){  <span style="white-space:pre">        </span>   // 判断开始时是否在箱子周围   
            if(box_x-per_x==dx[i] && box_y-per_y==dy[i])  {    
                ok=true;  break;    
            }  
        }    
        if(!ok){               // 判断能否到达箱子周围   
            G[box_x][box_y]=1; <span style="white-space:pre">       </span>   // 人无法穿过箱子**    
            for(int i=0;i<4;i++){    
                aim_x=box_x+dx[i];  aim_y=box_y+dy[i];    
                ok=judge_per(per_x,per_y,aim_x,aim_y);    
                if(ok){ per_x=aim_x;  per_y=aim_y;  break; }    // 能到达箱子周围让person的位置更新一下即可   
            }   
            G[box_x][box_y]=0;    
        }    
        if(!ok) {printf("-1\n");continue;}    
        memset(state,0,sizeof(state));  
        printf("%d\n",judge_box());    
    }    
    return 0;    
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值