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-3
117             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          else
154           printf("Case %d: -1\n",T);
155         }
156         else
157          printf("Case %d: 0\n",T);
158         T++;
159     }
160     return 0;
161 }
时间: 2024-08-06 15:43:19

Poj1324(BFS+剪枝)的相关文章

POJ 3278 Catch That Cow(BFS 剪枝)

题目链接:http://poj.org/problem?id=3278 这几次都是每天的第一道题都挺顺利,然后第二道题一卡一天. = =,今天的这道题7点40就出来了,不知道第二道题在下午7点能不能出来.0 0 先说说这道题目,大意是有个农夫要抓牛,已知牛的坐标,和农夫位置.而且农夫有三种移动方式,X + 1,X - 1,X * 2,问最少几步抓到牛. 开始认为很简单的,三方向的BFS就能顺利解决,然后在忘开标记的情况下直接广搜,果然TLE,在你计算出最少位置之前,牛早跑了. 然后反应过来开标记

SPOJ 206 BITMAP(BFS+剪枝)

SPOJ 206 BITMAP(BFS+剪枝) ACM 题目地址:SPOJ 206 BITMAP 题意: 给出一个矩阵,有黑白点,计算每个点离最近的白点的距离,p1=(i1,j1) and p2=(i2,j2),距离d(p1,p2)=|i1-i2|+|j1-j2|. 分析: 有剪枝的BFS,如果从黑色的开始进行BFS最近的白色,复杂度是O(n^4),复杂度无法接受. 于是想把黑色白色分开记录下来,然后两遍for,发现复杂度不变... 从黑色开始搜,如果要记忆化的话,貌似很麻烦,但是我们可以从白色

HDU-1226 超级密码 (BFS+剪枝)

Problem Description Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息:密 码是一个C进制的数,并且只能由给定的M个数字构成,同时密码是一个给定十进制整数N(0<=N<=5000)的正整数倍(如果存在多个满足 条件的数,那么最小的那个就是密码),如果这样的密码存在,那么当你输入它以后门将打开,如果不存在这样的密码......那就把门炸了吧.注意:由于宝藏的历史久远,当时的系统最多只能

ppoj 3278搜索bfs+剪枝

http://poj.org/problem?id=3278 大致题意: 给定两个整数n和k 通过 n+1或n-1 或n*2 这3种操作,使得n==k 输出最少的操作次数 解题思路: 说实话,要不是人家把这题归类到BFS,我怎么也想不到用广搜的= = 自卑ing... 水题水题,三入口的BFS 注意的地方有二: 1.  由于用于广搜的 队列数组 和 标记数组  相当大,如果定义这两个数组时把它们扔到局部去,编译是可以的,但肯定执行不了,提交就等RE吧= = 大数组必须开为 全局 ...常识常识.

hdu 5040 优先队列BFS+剪枝

(北京网络赛09题)题意:给一矩阵(图),里面有起点,终点,还有探照灯(每个有初始朝向,每秒顺时针转90度),前面有灯或者自己被灯照着,移动就要花3秒,求起点到终点最短时间. 用一个数组三维数组记录一下,用来当前位置当前时间%4有没有灯,然后优先队列(时间短的在前面),搜索即可.考虑到可以来回走或者原地等,不能简单判重剪枝:每个地方最多是4种状态!就是4秒之后就全图状态回到一样!所以若当前状态(时间%4)下来过就不用来了. #include<iostream> #include<vect

HDU 2433 (最短路+BFS+剪枝)

http://acm.hdu.edu.cn/showproblem.php?pid=2433 这个问题因为路径都是1,所以可以用bfs遍历 可以看这几篇文章讲解: http://blog.csdn.net/panyanyany/article/details/7215069 (这篇代码非常清晰,而且效率很高) http://www.cppblog.com/keroro/archive/2013/05/27/200622.html?opt=admin 1 #include <cstdio> 2

HDU 5652 India and China Origins 二分优化+BFS剪枝

题目大意:给你一个地图0代表可以通过1代表不可以通过.只要能从第一行走到最后一行,那么中国与印度是可以联通的.现在给你q个点,每年风沙会按顺序侵蚀这个点,使改点不可通过.问几年后中国与印度不连通.若一直联通输出-1. 题目思路:看数据这道题就是卡时间的,我们的基本思路是每当风沙侵蚀一个点,我们就进行一次广搜,看看图上下是否联通,我们应尽可能的去优化这个过程. 所以我们可以在遍历年的时候用二分查找: 若当年图可以上下联通,则继续向右查找 若当年图上下无法联通,则向左查找 剪枝: 为了省时间我们应该

POJ 3126 Prime Path (BFS+剪枝)

题目链接:传送门 题意: 给定两个四位数a,b,每次可以改变a的任意一位,并且确保改变后的a是一个素数. 问最少经过多少次改变a可以变成b. 分析: BFS,每次枚举改变的数,有一个剪枝,就是如果这个改变的数之前已经得到过了, 就没有必要继续变回它了. 代码如下: #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <queue&g

【算法学习笔记】49.暴力穷举 BFS 剪枝 SJTU OJ 1357 相邻方案

相邻方案 Description 有一个5*5的矩阵,每个元素只可能是H或者J. 我们现在需要选择7个相邻的格子使得H的数量小于J的数量.其中,拥有公共边的两个格子可以被称为相邻的格子. 对于任意一种输入的5*5矩阵,请输出满足上述条件的方案总数. Input Format 共5行,表示矩阵情况.(每一个元素只可能是H或J) Output Format 一个整数N,代表不相同方案的总数. Input Sample HHHHH JHJHJ HHHHH HJHHJ HHHHH Output Samp