博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 贪吃蛇

贪吃蛇

Description

身长为L的贪吃蛇在一个有障碍的N*M的格子中游走,问最少用多少步才能让贪吃蛇的蛇头到达(1,1)。

Input

第一行三个正整数 N, M,L。L表示贪吃蛇的长度。
接下来 L 行,顺序描述贪吃蛇每节身体的位置。每行两个正整数 X,Y。 表示某节身体的位置,按蛇头到蛇尾的顺序描述。
接下来一个正整数K。 表示有 K个障碍,每个障碍占一个格子。
接下来K行, 每行两个正整数 X,Y, 表示某个障碍的位置。

Output

一个整数,表示到达格子(1,1)最少的步数。(给定数据保证能够到达,并且蛇头移动的目标格子必须是空的。)

Sample Input

5 6 4 
4 1 
4 2 
3 2 
3 1 

2 3 
3 3 
3 4

Sample Output

9

Hint

对于 100% 的数据,2≤N、M≤20,2≤L≤8

Source

搜索,A*搜索,状态压缩

 

解析

这题一看就要用搜索啊啊啊!!!

然而仔细一想,却有很多问题,其中主要有(因为是本人遇到的)

  • 蛇走的时候障碍物是变化的,每走一步都要更新,因此地图走起来就很繁琐(本蒟蒻一开始还用了三维数组)。
  • 如果暴搜的话,空间会炸掉(循环队列也一样)。

那么,让我们仔细想想。。。

首先,蛇的位置并不需要O(L)一个个修改。

假设,下面是一条蛇(@为蛇头)

########@

那么,它向前走一步后就变成了

   ########@

也就是说,原来蛇尾的部分没有了,原来蛇头前面多了一部分。

所以,只需要记录下每个部分在蛇头位置时,所对应的蛇尾部分(也就是说,蛇头是不断在由不同的部分替换着的),

再修改蛇尾及地图就行了。

具体代码如下:

int x1=t[t[p].net].x;    //t[p].net表示当p部分为蛇头时蛇尾是哪部分 
int y1=t[t[p].net].y;
t[t[p].net].x=x;
t[t[p].net].y=y;
s[x][y]=1;s[x1][y1]=0;  //x和y代表将要更新的蛇头的位置,s为地图 

 

然后,我们再枚举需要的步数,并dfs判断能否到达(1,1)就行了。

详细讲解请看代码:

#include <cstdio>
#include <iostream>
using namespace std;

struct snake{
    int x,y,net;
};
struct hh{
    int x,y;
}e[1001];
int n,m,k,l;
int map[21][21]/*地图的初始状态*/,w[21][21]/*(1,1)到个点的最短步数*/;
int s[21][21]/*移动中的地图状态*/;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int maxn/*规定步数*/,ok=0/*标记*/;
snake a[10]/*记录蛇的初始状态*/,t[10]/*蛇移动中的状态*/;

void bfs()/*简单的深搜*/{
    int h=1,t=1,que[100001][3]={0};
    que[1][0]=1;que[1][1]=1;
    w[1][1]=0x3f3f3f3f;
    while(h<=t){
        for(int i=0;i<4;i++){
            int x=que[h][0]+dx[i];
            int y=que[h][1]+dy[i];
             if(x>0&&y>0&&x<=n&&y<=m){
                 if(!map[x][y]&&!w[x][y]){
                     w[x][y]=que[h][2]+1;
                     que[++t][0]=x;
                     que[t][1]=y;
                     que[t][2]=w[x][y];
                 }
                 
             }
        }
        h++;
    }
    w[1][1]=0;
}

void dfs(int p/*当前作为蛇头的部分*/,int d/*已走步数*/){
    snake v=t[p];  //便于书写 
    if(d+w[v.x][v.y]>maxn) return ;//当前位置到(1,1)的最短步数+已走的步数大于规定步数时,直接返回 
    else if(d+w[v.x][v.y]==maxn)/*规定步数走到(1,1)*/{
        if(v.x==1&&v.y==1){
            ok=1;//标记 
            return ;
        }
    }
    for(int i=0;i<4;i++)/*和深搜类似*/{
        if(ok) return ;
        int x=v.x+dx[i];
        int y=v.y+dy[i];
        if(x>0&&y>0&&x<=n&&y<=m){
            if(!s[x][y]){
                int x1=t[t[p].net].x;  //t[p].net表示当p部分为蛇头时蛇尾是哪部分 
                int y1=t[t[p].net].y;
                t[t[p].net].x=x;
                t[t[p].net].y=y;
                s[x][y]=1;s[x1][y1]=0;  //x和y代表将要更新的蛇头的位置,s为地图 
                dfs(t[p].net,d+1);
                t[t[p].net].x=x1;
                t[t[p].net].y=y1;
                s[x][y]=0;s[x1][y1]=1;  //回溯 
                
            }
        }
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&l);
    for(int i=1;i<=l;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    a[1].net=l;
    for(int i=2;i<=l;i++) a[i].net=i-1;  //记录i为蛇头时i-1为蛇尾 
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d%d",&e[i].x,&e[i].y);
        map[e[i].x][e[i].y]=1;
    }
    //map记录了障碍物的位置 
    bfs();  //用bfs求出(1,1)在不考虑蛇身的情况下到达每个点的最短步数 
    for(int i=1;i<=l;i++){
        map[a[i].x][a[i].y]=1;
    }//bfs后将蛇身的初始位置加入障碍物中 
    for(maxn=1;ok==0/*还未走到过(1,1)*/;maxn++)/*枚举规定步数*/{
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                s[i][j]=map[i][j];
            }
        }/*每次将地图还原成初始的状态*/
        for(int i=1;i<=l;i++) t[i]=a[i]/*还原蛇身初始状态*/;
        dfs(1,0)/*判断能否到达(1,1)*/;
    }
    printf("%d\n",maxn-1/*因为最后会多加一下*/);
}

 

posted @ 2019-03-02 22:13  Hastin  阅读(522)  评论(0编辑  收藏  举报