zoukankan      html  css  js  c++  java
  • 题解 贪吃蛇

    贪吃蛇

    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
    ",maxn-1/*因为最后会多加一下*/);
    }
  • 相关阅读:
    模板方法模式
    LINQ多条件OR模糊查询
    在LINQ中实现多条件联合主键LEFT JOIN
    js只显示整点
    Vue-cli2中处理跨域
    vue动态绑定类(实现tab)
    Vue中引入cdn同时防止cdn挂掉
    vue+nginx开启gzip压缩
    Vue路由守卫
    vue-router嵌套路由和二级目录(域名)
  • 原文地址:https://www.cnblogs.com/zsq259/p/10463273.html
Copyright © 2011-2022 走看看