Snake HDU - 2605

题目链接: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 :

  1. The length of the snake’s body won’t change even though it eat a magic bean.
  2. 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.)
  3. 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
    Figure 1
    Figure 2

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盐太郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值