POJ 1324 [Holedox Moving] 状态压缩BFS
题目链接:http://poj.org/problem?id=1324
题目大意:n*m网格里有蛇和障碍,蛇只能向空格处移动,不能撞到自己,问到(1,1)的最短步数,如无法到达输出-1.
关键思想:不能直接对蛇头进行BFS,因为蛇身对蛇头的决策也有影响,故BFS时应该保存蛇身状态,我们放在state里用位运算来做。因为根据蛇头和每一节延伸的方向(东南西北)我们可以还原出整条蛇。比较坑的是这道题用pow会超时,自己写一个好啦。
由于vis数组很大,如果对于每个Case都初始化vis数组浪费时间,所以在申请了全局变量vis之后,我们对于每个kase,都只用当前的kase值去标记vis表示当前状态已出现.
#include <iostream> #include <string.h> #include <cstdio> #include <queue> #include <cmath> using namespace std; typedef long long ll; int n,m,L,K; int Case=1; const int MAXN=21; const int MAXM=21; int M[MAXN][MAXM]; int dir[4][2]={0,1,1,0,0,-1,-1,0}; int vis[MAXN][MAXM][1<<14]={0};//蛇头在x,y的蛇状态 对应vis数组 struct point{ int x; int y; int step; int state;//二进制每两位表示一个方向(总共4个方向),共2*(L-1)位 }; int snake[10][2]; int POW(int a,int n){ int x=1; for(int i=0;i<n;i++){ x*=a; } return x; } void SnakeMap(point a){//将蛇的状态再现到地图上 int x=a.x,y=a.y; int haha=3*POW(4,L-2);//位运算注意理解哦~ int direction; M[x][y]=1; for(int i=0;i<L-1;i++){ direction=(a.state&haha)/POW(4,L-2-i); M[x+=dir[direction][0]][y+=dir[direction][1]]=1; haha >>= 2; } return; } void recover(point a){//复原 int x=a.x,y=a.y; int haha=3*POW(4,L-2); int direction; M[x][y]=0; for(int i=0;i<L-1;i++){ direction=(a.state&haha)/POW(4,L-2-i); M[x+=dir[direction][0]][y+=dir[direction][1]]=0; haha >>= 2; } return; } int BFS(){ point First; First.x=snake[1][0],First.y=snake[1][1],First.step=0,First.state=0; for(int i=2;i<=L;i++){ if(snake[i][0]-snake[i-1][0]==1){ First.state <<= 2; First.state+=1; }else if(snake[i][0]-snake[i-1][0]==-1){ First.state <<= 2; First.state+=3; }else if(snake[i][1]-snake[i-1][1]==1){ First.state <<= 2; First.state+=0; }else if(snake[i][1]-snake[i-1][1]==-1){ First.state <<= 2; First.state+=2; } }//对state的处理,位运算 vis[First.x][First.y][First.state]=Case; //初始化第一个格局 queue<point>q; q.push(First); point nw,nt; while(!q.empty()){ nw=q.front(); q.pop(); if(nw.x==1&&nw.y==1)return nw.step; SnakeMap(nw);//蛇状态再现在地图上 for(int i=0;i<4;i++){ nt.x=nw.x+dir[i][0],nt.y=nw.y+dir[i][1],nt.step=nw.step+1; nt.state=(nw.state/4+((i+2)%4)*POW(4,L-2));//公式是自己推的 if(nt.x<=n&&nt.x>=1&&nt.y<=m&&nt.y>=1&&M[nt.x][nt.y]==0&&vis[nt.x][nt.y][nt.state]!=Case){ vis[nt.x][nt.y][nt.state]=Case; q.push(nt); } } recover(nw);//还原地图 } return -1; } int main(){ while(scanf("%d%d%d",&n,&m,&L)==3&&(n&&m&&L)){ memset(M,0,sizeof(M)); memset(snake,0,sizeof(snake)); for(int i=1;i<=L;i++){ scanf("%d%d",&snake[i][0],&snake[i][1]); M[snake[i][0]][snake[i][1]]=2; } scanf("%d",&K); for(int i=0;i<K;i++){ int xx,yy; scanf("%d%d",&xx,&yy); M[xx][yy]=1; } //地图输入完毕 printf("Case %d: %d\n",Case++,BFS()); } return 0; }
边完善自己边认识自己