2017-03-11 贪吃蛇

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
3
2 3
3 3
3 4

Sample Output

9

Hint

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

Source

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

Solution

直接BFS,不过这里的vis状态表示要注意。由于蛇最长为8格。所以我们用vis[21][21][1<<14] 表示蛇的这种状态是否已经出现.其中vis的前两维表示蛇头的坐标,后一维的二进制形式的每2位(可表0-3)表示从1到L-1开始该蛇的身体在该蛇身体的前一格的方向(后一格相对于前一格的方向).这样我们就能用最小的空间表示完整个蛇在迷宫的状态了.
现在要求最小距离,我们不用dist[][][]了,因为太耗空间了.我们用Node节点,Node中有x,y,st,dist 4个属性,前3个属性对应vis数组的前三维.最后一个属性是当前状态的最小距离.
在BFS扩展的时候,对于4个方向,计算得到nx和ny,然后判断nx和ny是否越界,是否是障碍,是否会与蛇的旧身体位置冲突.如果以上情况都不会发生,那么就生成了一个蛇与迷宫的新状态.
注意这里记录的st状态中的方向是指,后面一个格子(蛇的身体)处于前面一个格子(蛇的身体)的哪个方向.所以在BFS的时候,那个d要取反方向才能加到新的nst中.具体实现见代码.
思路2:对蛇身进行哈希判重,同样也要注意,下一步不能是前一个状态蛇身上的节点。http://www.cnblogs.com/yongze103/archive/2010/10/05/1843538.html

Code

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<queue>  
using namespace std;  
const int maxr=21;  
const int maxc=21;  
int vis[maxr][maxc][1<<14];//vis第三维的高位二进制表示紧接着蛇头的那格在蛇头的相对方向  
int maze[maxr][maxc];  
int R,C;  
struct Node//注意这里记录的st状态中的方向是指,后面一个格子处于前面一个格子的哪个方向  
{  
    int x,y;  
    int st,dist;  
    Node(int x,int y,int st,int dist):x(x),y(y),st(st),dist(dist){}  
};  
int L;//蛇长  
int dir[10];//用来保存解码后的方向  
int dx[]={1,0,-1,0};//下,左,上,右  
int dy[]={0,-1,0,1};  
bool check(int x,int y,Node node)//判断x,y坐标是否处于node状态的蛇身 , 下一步的蛇头 
{  
    for(int i=L-1;i>=1;i--)  
    {  
        dir[i]=node.st&3;//提取相对方向  
        node.st>>=2;  
    }  
    int xx=node.x,yy=node.y;  
    for(int i=1;i<L;i++)  
    {  
        xx+= dx[dir[i]];  
        yy+= dy[dir[i]];  
        if(xx==x&&yy==y) return true;//表示冲突  
    }  
    return false;//表示无冲突  
}  
queue<Node> Q;  
int BFS(Node nod)  
{  
    if(nod.x==1&&nod.y==1) return 0;  
    while(!Q.empty())Q.pop();  
    Q.push(nod);  
    vis[nod.x][nod.y][nod.st]=1;  
    while(!Q.empty())  
    {  
        Node node=Q.front();Q.pop();  
        int x=node.x,y=node.y,st=node.st,dist=node.dist;  
        for(int d=0;d<4;d++)  
        {  
            int nx=x+dx[d],ny=y+dy[d];  
            if(nx==1&&ny==1) return dist+1;  
            if(nx<1||nx>R||ny<1||ny>C||maze[nx][ny]==1||check(nx,ny,node)) continue;  
            int ndist=dist+1,nst=(st>>2)+( ((d+2)%4)<<(2*(L-2)));//新距离,新状态,注意这里(d+2)%4表示对d取反向  
            if(vis[nx][ny][nst]==1)continue;//之前走过  
            Q.push(Node(nx,ny,nst,ndist));  
            vis[nx][ny][nst]=1;  
        }  
    }  
    return -1;  
}  
int main()  
{  
    while(scanf("%d%d%d",&R,&C,&L)==3)  
    {  
        if(R==0&&C==0&&L==0) break;  
        int x,y,nx,ny;  
        Node node(0,0,0,0);  
        scanf("%d%d",&node.x,&node.y);  
        x=node.x,y=node.y;  
        for(int i=1;i<L;i++)  
        {  
            scanf("%d%d",&nx,&ny);  
            for(int d=0;d<4;d++)  
            {  
                if(x+dx[d]==nx && y+dy[d]==ny)  
                {  
                    node.st = (node.st<<2)+d;  
                    break;  
                }  
            }  
            x=nx,y=ny;  
        }  
        int blocks;  
        scanf("%d",&blocks);  
        memset(maze,0,sizeof(maze));  
        for(int i=1;i<=blocks;i++)  
        {  
            scanf("%d%d",&x,&y);  
            maze[x][y]=1;  
        }  
        printf("%d\n",BFS(node));  
    }  
    return 0;  
}  

其实,广搜也很OK

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值