hdu 1254 推箱子

我只说一下别人讲的不清楚的,为什么一个点的某个方向不可以走两次?

实际上,这种说法和判断一个局面和前面重复是等价的。只不过后者是直观的说法,前者是一种“后知后觉”的说法,是建立在事情的结果上的,但是在这里,事情的结果显然具有更简单的情况,所以我们用事件的结果代表它本身。

比如如下三种情况:

可以很容易判断出来,他们是同一种情况,但是我们是怎么判断出来的,直觉?

用自然语言描述,就是说小人又可以把(4,3)点的箱子向左(右)推,注意这里一个“又”字,其实我们不自觉的就把判定方法建立在了结果之上,而不是这个局面本身,因为局面本身十分复杂,但结果只是这一个!所以即是说一个点的某方向不可以走两次。

广搜是无权图的最短路,如果我们可以在最短路中绕圈,则可以省去这个圈,与它是最短路矛盾,所以我们要判重!

最后它的最大地图是7*7,也就是49个格,如果存在一个解,显然最多是走遍每一个点的每个方向,一共49*4=196步,根据抽屉原理,最多在第197步会出现环路,这是不可能的,所以我们完全可以用unsigned char 存储步数。在此题表现的不是很明显,但是对于一些其他的,因为广搜几乎是指数爆炸式的增长数据,所以初始节点稍微改变一下条件,就可以在以后的以后刮起一场龙卷风!

#include <cstdio>
#include <cstring>
#include <queue>
struct point{
    unsigned char x0,y0;
};
struct pro{
    unsigned char x0,y0,x1,y1;
    int step;
};
std::queue<point> q2;
std::queue<pro>     q1;
int t,m,n,u1;
char map[10][10],dirr[10][10][4],temp[10][10],c[5];
char dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
unsigned char h1,h2,flag;
bool path(point a,point b){
    if((a.x0==b.x0)&&(a.y0==b.y0))  return true;
    int i,j;
    point p1,p2;
    memcpy(temp,map,sizeof(map));
    j=q2.size();
    for(i=0;i<j;i++)    q2.pop();
    h1=a.x0;h2=a.y0;
    p1.x0=h1;p1.y0=h2;
    temp[h1][h2]=1;
    q2.push(p1);j=0;
    while(!q2.empty()){
        p1=q2.front();
        q2.pop();
        for(i=0;i<4;i++){
            h1=p1.x0+dir[i][0];
            h2=p1.y0+dir[i][1];
            if(temp[h1][h2])    continue;
            p2.x0=h1;p2.y0=h2;
            if((h1==b.x0)&&(h2==b.y0))  {j=1;return true;}
            temp[h1][h2]=1;
            q2.push(p2);
        }
    }
    if(!j)  return false;
    return false;
}
point p1,p2,p3;
pro r1,r2,e;
int main(){
    scanf("%d",&t);
    while(t--){
        memset(map,1,sizeof(map));
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                scanf("%s",c);
                c[0]-='0';
                if(*c==2)  {r1.x0=i;r1.y0=j;}
                else if(*c==3)  {e.x0=i;e.y0=j;}
                else if(*c==4)  {r1.x1=i;r1.y1=j;}
                if(*c>1) *c=0;
                map[i][j]=*c;
            }
        }
        memset(dirr,0,sizeof(dirr));
        u1=q1.size();
        for(int i=0;i<u1;i++)   q1.pop();
        r1.step=0;q1.push(r1);flag=0;
        while(!q1.empty()){
            r1=q1.front();
            q1.pop();
            map[r1.x0][r1.y0]=1;
            p1.x0=r1.x1;p1.y0=r1.y1;
            for(int i=0;i<4;i++){
                p2.x0=r1.x0+dir[i][0];p2.y0=r1.y0+dir[i][1];
                p3.x0=r1.x0+dir[3-i][0];p3.y0=r1.y0+dir[3-i][1];
                if(map[p2.x0][p2.y0]||map[p3.x0][p3.y0])    continue;
                if(path(p1,p2)){
                    if(p3.x0==e.x0&&p3.y0==e.y0){
                        flag=1;
                        printf("%d\n",r1.step+1);
                        break;
                    }
                    dirr[i][r1.x0][r1.y0]++;
                    if(dirr[i][r1.x0][r1.y0]==1){
                        r2.x0=p3.x0;r2.y0=p3.y0;r2.x1=r1.x0;r2.y1=r1.y0;r2.step=r1.step+1;
                        q1.push(r2);
                    }
                }
            }
            if(flag)    break;
            map[r1.x0][r1.y0]=0;
        }
        if(flag)    continue;
        printf("-1\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值