首页 > 代码库 > 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+剪枝)