首页 > 代码库 > Poj1324(BFS+剪枝)

Poj1324(BFS+剪枝)

剪枝好题.

题目大意:

给一个地图,有一条蛇,给定从头到尾的各个点的坐标,地图上有些点是不能走的,然后就是跟以前玩过的贪吃蛇的规则一样了,蛇头不能碰到自己,问至少要多少步才能让蛇头从起点到达终点.地图长宽都是20以内,蛇长范围(2~8)

思路:

求最少步数,用bfs,图并不大,但是需要记录蛇的状态,还要判断重复.仔细观察,并不需要记录整个蛇各个点的坐标,也不需要把地图hash当做状态.我们要记录的状态无非就是蛇的位置.

那么其实也就是蛇头的坐标+蛇尾的相互关系.因为蛇是一个整体,身体的每一个部分都是相连的.那么可以只记录一个蛇头的坐标,然后后面最多也就7个长度,每一个都是(1-4)中的一个,代表相对于前一节身体的相对坐标,那么只需要一个used[21][21][1<<14]就可以记录整个蛇的状态了.即使是这样也有20*20*16384=6553600个状态.

在网上看到一种比较好的剪枝.

首先步数有没有一个上界呢,也就是说我们在bfs的过程中,如果步数比这个提前算出来的上界还要差的话,根本不可能成为最优解.

首先,蛇在前进的过程中会把它的身体逐渐的放到自己的后面(有可能它要走的方向的前面挡着它自己的身体).那么也就是说它的身体只能最多成为一次它前进道路上的障碍.或许更少,因为移动一步,它的身体就会有向它身后移动的趋势,原来是障碍的身体有可能就变成了空地.所以把它的身体当成永久的障碍然后让蛇头bfs一遍,看到达终点需要多少步,这个步数就是上界.当然也有可能到不了终点,到不了终点不能说明它的身体随着它的头一起移动之后也到不了终点,也因此在这种情况下不能成为判断下界的标准,需要特判.

还有一个是把蛇头以外的身体都当成空地,这样一定是一个下界.(没实际作用.)

在任何一个时刻,如果一个状态现在所在的点用的步数a加上这个点到终点的曼哈顿距离b的和a+b>上界 这个点不可能是最优解.

综上,附上AC代码:

  1 #include <iostream>  2 #include <cstdio>  3 #include <string.h>  4 #include <stdlib.h>  5 #include <math.h>  6 #include <queue>  7 using namespace std;  8 #define maxn 21  9 struct pos{ 10    int x,y; 11    pos(int xx,int yy):x(xx),y(yy){} 12    pos(){} 13 }; 14 struct posdep{ 15    int x,y,dep; 16    posdep(int xx,int yy,int dd):x(xx),y(yy),dep(dd){} 17    posdep(){} 18 }; 19 struct snake{ 20    int x,y,dep,tail; 21    snake(int xx,int yy,int dd,int tt):x(xx),y(yy),dep(dd),tail(tt){} 22    snake(){} 23 }; 24 bool mapp[maxn][maxn],preused[maxn][maxn],used[maxn][maxn][1<<14]; 25 int n,m,l,k; 26 const int Dir[4][2]={-1,0,0,1,1,0,0,-1}; 27 bool in_bounce(int x,int y) 28 { 29     if(x<=n&&x>=1&&y<=m&&y>=1) return 1; 30     return 0; 31 } 32 int calstep(int x,int y) 33 { 34     deque<posdep> q; 35     if(x==1&&y==1) return 0; 36     q.push_back(posdep(x,y,0)); 37     preused[x][y]=1; 38     bool sign=0; 39     posdep now; 40     while(!q.empty()) 41     { 42         now=q.front(); 43         for(int i=0;i<4;i++) 44         { 45          int newx,newy; 46          newx=now.x+Dir[i][0],newy=now.y+Dir[i][1]; 47          if(!in_bounce(newx,newy)||mapp[newx][newy]||preused[newx][newy]) continue; 48          if(newx==1&&newy==1) {sign=1;break;} 49          q.push_back(posdep(newx,newy,now.dep+1)); 50          preused[newx][newy]=1; 51         } 52         if(sign) break; 53         q.pop_front(); 54     } 55     if(sign) return now.dep+1; 56     return -1; 57 } 58 deque<snake> q; 59 int main() 60 { 61     //freopen("in.txt","r",stdin); 62     int minstep,maxstep; 63     int i,j,x,y,T=1; 64     pos body[10],block[405]; 65     while(scanf("%d%d%d",&n,&m,&l),n||m||l) 66     { 67         int tailnum=0; //初始的尾部值 68         while(!q.empty()) q.pop_front(); 69         memset(mapp,0,sizeof(mapp)); 70         memset(preused,0,sizeof(preused)); 71         memset(used,0,sizeof(used)); 72         for(i=1;i<=l;i++) 73         { 74           scanf("%d%d",&body[i].x,&body[i].y); 75         } 76         for(i=1;i<=l;i++) 77           if(i!=1) 78           { 79               for(j=0;j<4;j++) 80                if(body[i].x+Dir[j][0]==body[i-1].x&&body[i].y+Dir[j][1]==body[i-1].y) tailnum=(tailnum<<2)+j; 81           } 82         scanf("%d",&k); 83         for(i=1;i<=k;i++) 84         { 85             scanf("%d%d",&x,&y); 86             block[i].x=x,block[i].y=y; 87             mapp[x][y]=1; 88         } 89         //只考虑头部走到11的步数下限 90         minstep=calstep(body[1].x,body[1].y); 91         memset(preused,0,sizeof(preused)); 92         memset(mapp,0,sizeof(mapp)); 93         for(i=2;i<=l;i++) 94          mapp[body[i].x][body[i].y]=1; 95         for(i=1;i<=k;i++) 96          mapp[block[i].x][block[i].y]=1; 97         //把尾部都当成障碍的步数上限 98         maxstep=calstep(body[1].x,body[1].y); 99         memset(mapp,0,sizeof(mapp));100         for(i=1;i<=k;i++)101          mapp[block[i].x][block[i].y]=1;102         q.push_back(snake(body[1].x,body[1].y,0,tailnum));103         used[body[1].x][body[1].y][tailnum]=1;104         bool sign=0;105         snake now;106         if(body[1].x!=1||body[1].y!=1)107         {108          while(!q.empty())109          {110             now=q.front();111             //剪枝开始预估一下距离112             int dis;113             dis=now.x-1+now.y-1+now.dep;114             if(maxstep!=-1&&dis>maxstep) {q.pop_front();continue;}115             //剪枝结束116             //0-2 1-3117             int tailinf=now.tail;118             int bd[10]; //准备保存提取出来的尾部信息119             bool dir[4]; //标记头部的四个方向是否可以走120             dir[0]=dir[1]=dir[2]=dir[3]=1;121 122             for(i=1;i<l;i++) //根据尾部的信息提取出尾部的坐标123             {124                 bd[l-i]=tailinf&3;125                 tailinf>>=2;126             }127             x=now.x,y=now.y;//头部坐标128             for(i=1;i<l;i++)129             {130               x=x+Dir[(bd[i]+2)%4][0],y=y+Dir[(bd[i]+2)%4][1];131               for(j=0;j<4;j++)132                if(now.x+Dir[j][0]==x&&now.y+Dir[j][1]==y) dir[j]=0;133             }134             for(i=0;i<4;i++)135              if(!in_bounce(now.x+Dir[i][0],now.y+Dir[i][1])||mapp[now.x+Dir[i][0]][now.y+Dir[i][1]]) dir[i]=0;136             //前一个部分在后一个部分的哪个方向137             for(i=0;i<4;i++)138              if(dir[i])139              {140                int tailinf=(now.tail>>2)+(i<<(2*(l-2)));141                if(now.x+Dir[i][0]==1&&now.y+Dir[i][1]==1) {sign=1;break;}142                if(!used[now.x+Dir[i][0]][now.y+Dir[i][1]][tailinf])143                {144                 q.push_back(snake(now.x+Dir[i][0],now.y+Dir[i][1],now.dep+1,tailinf));145                 used[now.x+Dir[i][0]][now.y+Dir[i][1]][tailinf]=1;146                }147              }148             if(sign) break;149             q.pop_front();150          }151          if(sign)152           printf("Case %d: %d\n",T,now.dep+1);153          else154           printf("Case %d: -1\n",T);155         }156         else157          printf("Case %d: 0\n",T);158         T++;159     }160     return 0;161 }

 

Poj1324(BFS+剪枝)