您的位置:首页 > 其它

【bzoj2284】【SDOI2011】贪吃蛇【搜索】【位运算】【卡常大法好】

2015-03-09 16:29 302 查看
这道题真是太精妙了……

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2284

首先这题棋盘的范围是15不是12。

本来原题是有special Judge的,因为要输出方案……

但是这是oj嘛。。就输出最短时间好了- -

我一开始愚蠢的想法是xy各用一个char存,蛇长最多为8,就开个结构体数组,再开一个长度为4的记录事物的位置……然后还有什么当前时间,剩余等待时间。。。

因为bfs相当于在边权为1的图上做最短路,所以如果直接转移到旁边的格子会导致不一定最优解。

所以我们需要设置一个等待时间,当它不为零时就留在原地不动。。

然后写代码的时候犯了许许多多SB错误……

比如,判断蛇是否自交要在走完这步之后,然而我写成了这样:

if(can(pos.i,pos.j))
// !!!!!!!!!!willnoteatitself不能放在这里!
{/*扩展状态。。。*/


……

然后哈希判重什么弄得很混乱,我把蛇形态一样位置一样的都视为相同的状态了……

反正各种混乱……

我觉得学习标程是必要的- -

于是我就看了题解。

真是太精妙了啊!!!!

std把队列节点的信息浓缩到了一个int里。。。

他是基于这样一种神奇却又真实的事实:只要蛇头、蛇的形态确定了,这条蛇在棋盘上就确定了。

所以可以预处理出蛇的所有合法形态,再加上蛇头坐标就能表示一条蛇了。

预处理也是bfs- -

怎么预处理呢?

首先确定蛇最多能有多少种形态。

把蛇头到蛇尾划为1、2、3、4…

先不管合不合法,2相对1的位置有4种(上下左右),3相对2的位置最多3种,4相对3的位置最多3种……

把蛇的各种长度下的各种形态加起来,最大不超过4×(32+33+34+35)=14404 \times(3^2 + 3^3 + 3^4 + 3^5 ) = 1440

蛇身的每一个部分相对于前一个部分的位置可以用2个bit表示。蛇头不必表示(因为蛇头是最前面的),可以用这两个bit存这条蛇的长度(长度就四种情况4、5、6、7, 8的时候已经到了目标状态,可以出解了,所以不必存),获取长度的时候加上4就行了。

预处理完后就可以把具体状态丢掉了,只存这个状态在队列中的下标(<211\lt 2^{11})(精妙啊!)

等待时间有7种(因为1≤能走的Aij≤81\le 能走的A_{ij} \le 8)可以用3个bit存。

然后食物的状态(吃了还是没吃)要4个bit

棋盘上的坐标可以用两个4bit一共8个二进制位存。

8+4+3+11=26.

代码里visit[ q[1] >> 3 ]里存的是蛇在这样的位置上有没有这样的等待时间,有的话则visit[q[1]>>3]里的第waitTime-1个二进制位就是1。

然后注意一下

int s1=mkStat(x1,y1,snakeStat1,foodStat1,waitTime1);
if(!(visit[s1>>3] & 1 << waitTime1)){
visit[s1>>3]|=1<< waitTime1;
q[++tail]=s1;
pred[tail]=head;
}


waitTime1就不用减一了……

下面贴上我这丑陋的代码……

(PS:队列不用初始化,这样能快很多……)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
const int maxr=17,maxc=17,maxq=15*15*1440*16*8,maxS=1<<11;
const int  dx[]={0,0,-1,1};
const int  dy[]={-1,1,0,0};
const char Dir[]={'L','R','U','D'};
int snakePos0[4][2];
int map[maxr][maxc],foodMap[maxr][maxc];
int stat[maxS][4],foodStat[maxS][4];
int q[maxq],pred[maxq];
unsigned char visit[1<<23];
char path[1000];
int ansTime,ansLink,foodCnt,pathLength,r,c;
int expand(int stat,int len,int k){
int tmp=stat;
len+=4;
k^=1;
int x=dx[k],y=dy[k];
for(int i=2;i<len;++i){
x+=dx[tmp&3];
y+=dy[tmp&3];
tmp>>=2;
if(!x && !y) return -1;
}
return (stat<<2 & ((1<<(len+len-2))-1)) | k;
}
void calcStat(){
//calculate the state of snake when it moves.
memset(stat,0xff,sizeof stat);
memset(foodStat,0xff,sizeof foodStat);
int head=0,tail=1;
int visit[1<<14]={0};
for(int i=3;i>0;--i){
int k=0;
while(snakePos0[i-1][0]+dx[k]!=snakePos0[i][0]||snakePos0[i-1][1]+dy[k]!=snakePos0[i][1])
++k;
q[1]=q[1]<<2|k;
}
q[1]<<=2;//头部没有“方向“,这两位是存储长度的。
visit[q[1]]=1;
while(head<tail){
int s0=q[++head];
int len0=s0&3;
int snakeStat0=s0>>2;
for(int k=0;k<4;++k){
int snakeStat1=expand(snakeStat0,len0,k);
if(snakeStat1>=0){
int s1=(snakeStat1 ) << 2 |len0;
if(!visit[s1]){
q[++tail]=s1;
visit[s1]=tail;
}
stat[head][k]=visit[s1];
}
}
//grow
for(int k=0;k<4;++k){
int snakeStat1=expand(snakeStat0,len0+1,k);
if(snakeStat1>=0){
if(len0<3){
int s1=(snakeStat1 ) << 2 |(len0+1);
if(!visit[s1]){//好像0xfff不加也行。。。
q[++tail]=s1;
visit[s1]=tail;
}
foodStat[head][k]=visit[s1];
}
else{
foodStat[head][k]=0;
//到达长度8,即到达目标状态
}
}
}
}
//  printf("%d %d",head,tail);//tail=1200!!!
}
#define mkStat(x,y,ss,fs,wt) (((x)<<22)|((y)<<18)|((ss)<<7)|((fs)<<3)|(wt))
void bfs(){//这里的 ss是上面calcStat中队列的下标
//  memset(q,0,sizeof q);
//  memset(visit,0,sizeof visit);
int head=0,tail=1;
q[1]=mkStat(snakePos0[0][0],snakePos0[0][1],1,(1<<foodCnt)-1,0);
visit[q[1]>>3]|=1<<0;
while(head<tail){
int s0=q[++head];
int x0          = s0 >> 22 & 0xf,
y0          = s0 >> 18 & 0xf,
snakeStat0  = s0 >>  7 & 0x7ff,
foodStat0   = s0 >>  3 & 0xf,
waitTime0   = s0 & 7;
if(!foodStat0 && !waitTime0){
ansLink=head;
//          printf("Anslink=%d\n",ansLink);
break;
}
if(waitTime0){
int s1=mkStat(x0,y0,snakeStat0,foodStat0,waitTime0-1);
if(!(visit[s1>>3] & 1<<(waitTime0-1))){
visit[s1>>3]|=1<<(waitTime0-1);
q[++tail]=s1;
pred[tail]=head;
}
}
else{
for(int k=0;k<4;++k){
int x1=x0+dx[k],
y1=y0+dy[k],
snakeStat1=stat[snakeStat0][k],
foodStat1=foodStat0,
waitTime1=abs(map[x1][y1]-map[x0][y0]);
if(!map[x1][y1]) continue;
if(foodMap[x1][y1]&&(foodStat1& 1<< (foodMap[x1][y1]-1))){
foodStat1^=1<<(foodMap[x1][y1]-1);
snakeStat1=foodStat[snakeStat0][k];
}
else snakeStat1=stat[snakeStat0][k];
if(snakeStat1>=0){
int s1=mkStat(x1,y1,snakeStat1,foodStat1,waitTime1);

if(!(visit[s1>>3] & 1<< waitTime1)){//不要减一!!不要减一!!
visit[s1>>3]|=1<< waitTime1;
q[++tail]=s1;
pred[tail]=head;
}
}
}
}
}
//  printf("%d\n",tail);
}
inline void input(){
scanf("%d%d",&r,&c);
char str[20];
for(int i=1;i<=r;++i){
scanf("%s",str);
for(int j=0;j<c;++j){
map[i][j+1]=str[j]-48;
}
}
for(int i=0;i<4;++i)
scanf("%d%d",&snakePos0[i][0],&snakePos0[i][1]);
int x,y;
scanf("%d",&foodCnt);
for(int i=1;i<=foodCnt;++i){
scanf("%d%d",&x,&y);
foodMap[x][y]=i;
}
}
inline void calcAns(){
bfs();
int p1=ansLink;
int x0,y0,x1,y1,wt;
x1=q[p1]>>22&0xf;
y1=q[p1]>>18&0xf;
while(p1>1){
int p0=pred[p1];
x0=q[p0]>>22 & 0xf;
y0=q[p0]>>18 & 0xf;
//      wt=q[p0]&7;
ansTime++;
/*      if(!wt){
int k=0;
while(x0+dx[k]!=x1||y0+dy[k]!=y1)
++k;
path[pathLength++]=Dir[k];
}
*/      x1=x0,y1=y0;
p1=p0;
}
//  reverse(path,path+pathLength);
}
inline void output(){
if(!ansLink)
puts("No solution.");
else{
printf("%d\n",ansTime);
//      for(int i=0;i<pathLength;++i)
//          putchar(path[i]);
//      putchar('\n');
}
}
int main(){
input();
calcStat();
calcAns();
output();
return 0;
}


搜索真是美丽啊!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: