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