题目链接:Snake HDU - 2605
===================================================
Snake
Time Limit: 6000 ms
Memory Limit: 32768 kB
Description
Snake is a popular game , and I believe most of us had played it . The Original game is that you can control a snake to eat the magic bean and after the snake eat one magic bean , the length of the snake’s body will get longer .But today we are talking about a new game. These are the rules of the new Snake Game :
- The length of the snake’s body won’t change even though it eat a magic bean.
- Some pairs of the beans have a relation , that is, one of them can not be eaten until another one had been eaten . We call the latter “the key bean” . For example , if A can’t be eaten until B had been eaten ,we say “B is the key bean of A”. (That means when A can’t be eaten , the snake can not move into the grid where A is.)
- The snake could not move to a wall or its body.Befor it move,it will chooses an adjacent vacant square of its head,which is neither a stone nor occupied by its body.
Figure 1 and figure2 shows how the snake move
Input
The first line contain a integer T (T <= 10).Followed by T cases. Each case contain five parts.
The first part: six integers ,H,W,L,K,R,N,(H <= 20 , W <= 20 , L <= 8 , K <= 7 ) means the height of the map , the width of the map , the length of the snake’s body, the number of the magic beans . the number of the relations , the number of the wall respectively.
The second part: L lines , each line contain two integer hi ,wi, indicating the original position of each block of snake’s body, from B1(h1,w1) to BL(hL,wL) orderly, where 1<=hi<=H, and 1<=wi<=W,1<=i<=L.
The third part: K lines ,each line contain two integer hi ,wi , indicating the position of each magic bean , from MB1(h1,w1) to MBK(hK,wK) orderly, where 1<=hi<=H, and 1<=wi<=W,1<=i<=K.
The fourth part : R lines , each line contain two integer A ,B means “A is the key bean of B ”. The A and B may appear several times , but “one bean will have only one key bean”.
The fifth part: N lines , each line contain two integer hi ,wi , indicating the position of each wall , from W1(h1,w1) to WN(hN,wN) orderly, where 1<=hi<=H, and 1<=wi<=W,1<=i<=N.
Output
For each case , if the snake could eat all the magic beans , output the minimum step it took. If the snake could not , just output “-1” (without the quotation marks) .
Sample Input
1
8 9
5 2
1 8
5 2
6 2
6 3
6 4
6 5
4 2
2 6
2 1
2 5
3 5
4 4
4 5
4 6
5 6
5 7
6 7
Sample Output
21
===================================================
题意:一条蛇在吃豆子,吃完豆子就会消失,且蛇身体不会变长,并且豆子之间存在关系,豆子必须在它的唯一关键豆子被吃掉的情况下才能被吃。蛇不能穿过自己的身体。
算法:状态压缩+BFS+剪枝
思路:
- 和 Holedox Moving POJ - 1324 思路一样。(建议先把这题做一遍)
- 判重,一开始我的想法是,vis[x][y][吃豆子状态][蛇身体状态],然后不出意外MLE了。后面我改用DFS,就TLE。然后尝试用DFS枚举所有吃豆子情况,然后每次吃豆子用Astar求最短路径,WrongAnser,所以这里中间吃豆子,最快并不是最优,因为存在蛇身状态的影响。
- 下面这个就是尝试的代码实现,结果WRONG
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1010;
int four[7];//四进制基数
int ditu[21][21];//石头数+地图标记
int H,W,snakeNum,stNum,beanNum,R;//蛇的长度
int snakeHeadX,snakeHeadY,snakeBody;//蛇头坐标 + 蛇形状的四进制表示(用四进制记录每一节对于前一节的方向)
bool vis[21][21][1<<14];//蛇头坐标 + 蛇形状的四进制表示 de 三维标记判重函数
int xx[] = {
0,1,0,-1};
int yy[] = {
1,0,-1,0};
int tx,ty,ans;
//用于求蛇身每一节对于前一节的方向,0,1,2,3,分别表示右下左上
int check(int a,int b,int c,int d){
if(c-a==0&&d-b==1) return 0;
if(c-a==1&&d-b==0) return 1;
if(c-a==0&&d-b==-1) return 2;
if(c-a==-1&&d-b==0) return 3;
}
struct bean{
int x,y,key=-1;
bean() {
}
bean(int a,int b,int c):x(a),y(b),key(c) {
}
}beans[8];
//蛇头X,Y坐标 蛇的四进制表示 启发函数数值f(这里是蛇头与洞口(1,1)的曼哈顿距离)
struct node{
int x,y,snake,step,f;
node(int xx,int yy,int ssnake,int sstep):x(xx),y(yy),snake(ssnake),step(sstep){
f=step+abs(x-tx)+abs(y-ty);
}
bool operator < (const node &a) const{
return f > a.f;
}
};
//这个函数是通过 蛇头坐标 蛇身四进制 来求 蛇身坐标数组 和 蛇尾对于前一节的方向
void snakeXY(int SX,int SY,int (&snakeX)[8],int (&snakeY)[8],int &c,int d){
int x = SX,y = SY;
for(int i=0;i<snakeNum-1;i++){
int tmp = d % 4; d/=4;
x+=xx[tmp],y+=yy[tmp];
snakeX[i] = x ,snakeY[i] = y ;
if(i==snakeNum-2) c = tmp;
}
}
//判断蛇头是否能走(x,y)
bool checkN(int x,int y,int a[],int b[],int c){
if(x<=0||x>H||y<=0||y>W||ditu[x][y]==-2) return false;
for(int i=0;i<snakeNum-1;i++){
if(x==a[i]&&y==b[i]) return false;
}
int z = ditu[x][y];
int zkey = beans[z].key;
if(z!=-1&&zkey!=-1&&((1<<zkey&c)==0)) return false;
return true;
}
//A*算法
int Astar(int sx,int sy,int snakeBody,int state,int &tSnakeBody){
memset(vis,false,sizeof vis);
priority_queue<node> q;
q.push(node(sx,sy,snakeBody,0));
vis[sx][sy][snakeBody] = true;
while(!q.empty()){
node p = q.top();q.pop();
if(p.x==tx&&p.y==ty) {
tSnakeBody=p.snake;return p.step;}
//这里记录蛇身坐标 和 蛇尾方向
int snakeX[8],snakeY[8],tail;
snakeXY(p.x,p.y,snakeX,snakeY,tail,p.snake);
//右、下、左、上
for(int i=0;i<4;i++){
int x = p.x + xx[i],y = p.y + yy[i];
if(!checkN(x,y,snakeX,snakeY,state)) continue;
//这里开始算新的方向四进制 去尾加头
int body = p.snake - four[snakeNum-2]*tail;
body*=4;
if(i==0) body+=2;
else if(i==1) body+=3;
else if(i==2) body+=0;
else body += 1;
if(vis[x][y][body]) continue;
vis[x][y][body] = true;
q.push(node(x,y,body,p.step+1));
}
}
return -1;
}
void init(){
cin>>H>>W>>snakeNum>>beanNum>>R>>stNum;
cin>>snakeHeadX>>snakeHeadY;
int a=snakeHeadX,b=snakeHeadY,c,d;
snakeBody=0;
for(int i=0;i<snakeNum-1;i++){
cin>>c>>d;
snakeBody+=four[i]*check(a,b,c,d);
a=c,b=d;
}
memset(ditu,-1,sizeof ditu);
for(int i=0;i<beanNum;i++){
cin>>a>>b;
ditu[a][b]=i;
beans[i].x=a,beans[i].y=b;
}
for(int i=0;i<R;i++){
cin>>a>>b;
beans[b-1].key=a-1;
}
for(int i=0;i<stNum;i++){
cin>>a>>b;
ditu[a][b]=-2;
}
}
void dfs(int snakeHeadX,int snakeHeadY,int snakeBody,int state,int step){
//cout<<snakeHeadX<<" "<<snakeHeadY<<" "<<snakeBody<<" "<<state<<" "<<step<<endl;
if((1<<beanNum)-1==state) {
ans=min(ans,step);return ;}
if(step>=ans||(1<<beanNum)-1<state) return ;
for(int i=0;i<beanNum;i++){
int x = beans[i].x , y = beans[i].y , key = beans[i].key;
//cout<<beans[i].x<<" "<<beans[i].y<<" "<<beans[i].key<<" ====="<<endl;
if(x==snakeHeadX&&y==snakeHeadY) continue;
if((1<<i&state)!=0) continue;
if(key!=-1&&((1<<key&state)==0)) continue;
//cout<<beans[i].x<<" "<<beans[i].y<<" "<<beans[i].key<<" "<<endl;
int snake;
tx = x, ty = y;
int anstep = Astar(snakeHeadX,snakeHeadY,snakeBody,state,snake);
//cout<<anstep<<endl;
if(anstep==-1) continue;
dfs(x,y,snake,state|1<<i,step+anstep);
}
return;
}
bool ok(int x,int y){
if(beans[x].key==y) return true;
if(beans[x