推箱子升级版——c++解法

22 篇文章 0 订阅

题目来自:ctf2019
题目描述:有最多不超过3个箱子,在不超过12
10的地图上有一个人,请把箱子推到对应的位置,求出最短路和路线
数据见代码末的input
状态:人+箱子的状态status 压缩成一个整数 显然在同一个status下,花费不同的距离到达这一个位置,我们要选取其中最短的那条
status的计算方法:整个地图抽象成进制 人的位置和箱子的位置抽象成一个数字 见cal函数 为了避免开过大的数组我们可以将最外围的墙扔掉可以节省不少空间。

#include <bits/stdc++.h>
using namespace std;
#define wall        8
#define human       4
#define box         2
#define destination 1
#define nothing 0
#define pb push_back
#define MAXN 20480000
int G[100][100];
vector<char>ans,tmp;
char s[5] = "awds";
//        a  w d s
int dx[]={0,-1,0,1};
int dy[]={-1,0,1,0};
int box_num = 0;
int vis[MAXN];
int box_x[4][MAXN];
int box_y[4][MAXN];
int row,col;
struct node
{
    int x,y;
    node(){}
    node(int _x,int _y):x(_x),y(_y){}
}myman;
vector<node> mybox;
vector<node> mydist;
int Min = 1<<20;
bool same(node a,node b){
    return a.x==b.x&&a.y==b.y;
}
bool success(int cnt){
    for(int i=0;i<box_num;++i){
        bool flag = false;
        for(int j=0;j<mydist.size();++j){
            if(same(node(box_x[i][cnt],box_y[i][cnt]),mydist[j])){
                flag=true;
                break;
            }
        }
        if(!flag) return false;
    }
    return true;
}
bool valid(int x,int y){
    return x>=0&&x<row&&y>=0&&y<col;
}
int getBoxId(int x,int y,int cnt){
    for(int i=0;i<box_num;++i){
        if(box_x[i][cnt]==x&&box_y[i][cnt]==y){
            return i;
        }
    }
    return -1;
}
int val(node t){
    return (t.x)*(col)+t.y;   //有效面积整体移到0,0起点
}
int cal(int tempX,int tempY,int cnt){
    int ret = val(node(tempX,tempY));
    for(int i=0;i<box_num;++i){
        ret = ret*(row)*(col)+val(node(box_x[i][cnt],box_y[i][cnt]));
    }
    return ret;
}
struct p
{
    int x,y,len,num;
    string path;
    p(){}
    p(int _x,int _y,int _len,int _num,string _path){
        x = _x;
        y = _y;
        len = _len;
        num = _num;
        path = _path;
    }
};
string path;
void bfs(int x,int y){
    queue<p> q;
    q.push(p(x,y,0,0,""));
    int cnt = 0;
    while(!q.empty()){
        p tmp = q.front();
        q.pop();
        if(success(tmp.num)){
            if(tmp.len<Min){
                Min = tmp.len;
                path = tmp.path;
            }
            continue;
        }
        for(int i= 0 ; i < 4; ++i){
            int fx = tmp.x+dx[i];
            int fy = tmp.y+dy[i];
            if(!valid(fx,fy)) continue;
            if(G[fx][fy]==wall) continue;
            int idx = getBoxId(fx,fy,tmp.num);
            if(idx>=0){
                if(G[fx+dx[i]][fy+dy[i]]==wall||getBoxId(fx+dx[i],fy+dy[i],tmp.num)>=0) continue;
                //推箱子
                ++cnt;//找个新的地方存放箱子的位置
                for(int j=0;j<box_num;++j){
                    box_x[j][cnt] = box_x[j][tmp.num];
                    box_y[j][cnt] = box_y[j][tmp.num];
                }
                box_x[idx][cnt] = fx+dx[i];//移动箱子
                box_y[idx][cnt] = fy+dy[i];
                int status = cal(fx,fy,cnt);//计算状态
                if (tmp.len<vis[status]){//状态一致且路径更短
                    vis[status] = tmp.len;
                    q.push(p(fx,fy,tmp.len+1,cnt,tmp.path+s[i]));
                }
                
            }
            else{
                int status = cal(fx,fy,tmp.num);
                if(tmp.len<vis[status]){
                    vis[status] = tmp.len;
                    q.push(p(fx,fy,tmp.len+1,tmp.num,tmp.path+s[i]));                    
                }
            }
        }
    }
}
int main(){
    cin>>row>>col;
    for(int i=0;i<row;++i){
        for(int j=0;j<col;++j){
            scanf("%1d",&G[i][j]);
        }
    } 
    row -= 2;
    col -= 2;
    memset(vis,0x3f,sizeof vis);
    for(int i=0;i<row;++i){
        for(int j=0;j<col;++j){
            G[i][j] = G[i+1][j+1];
            if(G[i][j]==box){
                mybox.pb(node(i,j));
            }
            else if(G[i][j]==destination){
                mydist.pb(node(i,j));
            }
            else if(G[i][j]==human){
                myman.x = i;
                myman.y = j;
            }
        }
    } 
    box_num = mybox.size();
    for(int i=0;i<box_num;++i){
        box_x[i][0] = mybox[i].x;
        box_y[i][0] = mybox[i].y;
    }
    bfs(myman.x,myman.y);
    cout<<path<<endl;
    return 0;
}



/*
(8:wall,4:human,2:box,1:destination)
Input 1:
5 10
8888888888
8000000008
8042010008
8000000008
8888888888

Output 1:
dd


Input 2:
12 10
8888888888
8000000008
8008000008
8000000808
8002000008
8000000108
8008808008
8810880008
8000000808
8000804208
8800000088
8888888888

Output 2:
wwddssaasawdwaaasawwwawwwddsasswdddd
Input 3:
5 10
8888888888
8000000008
8042000008
8000000018
8888888888

Output 3:
dddddwds


Input 4:
5 10
8888888888
8000000008
8041200008
8000000008
8888888888
Output 4:
wdddsa


Input 5:
9 10
8888888888
8888880088
8880200088
8800808288
8814110088
8880808008
8880002008
8880088888
8888888888
Output 5:
ddddssaawwwwddssaaaawwddssssasawwwddwwaasasdwwddssddsdsaaaasawwwddwwaasasddawwdddwdssssdsaaaasawwsddwwaawwddddssaaddwwaas


 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值