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].key==-1) return false;
    if(ok(beans[x].key,y)) return true;
    return false;
}

int main()
{
    four[0]=1;for(int i=1;i<7;i++) four[i] = four[i-1]*4;
    int _;cin>>_;
    while(_--){
        init();
        //for(int i=0;i<beanNum;i++) cout<<beans[i].x<<" "<<beans[i].y<<" "<<beans[i].key<<" "<<endl;
        //for(int i=1;i<=H;i++){
       //     for(int j=1;j<=W;j++) cout<<ditu[i][j]<<" ";cout<<endl;
        //}
        int flag = false;
        for(int i=0;i<beanNum;i++) if(ok(i,i)) {flag=true;break;}
        if(flag) {puts("-1");continue;}
        ans=INF;
        dfs(snakeHeadX,snakeHeadY,snakeBody,0,0);
        cout<<(ans==INF?-1:ans)<<endl;
    }
    return 0;
}

===================================================

请外援:实在没办法,求助我的大学同学(大牛),然后他在我软磨硬泡之下,尝试了几次,后面被他AC了。他的思路就是,维护一个三维vis[x][y][吃豆子状态]最优,蛇身体状态则用其他思路弥补。

他的代码:

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
    private static final FastReader cin = new FastReader(System.in);

    private static final int N = 21;
    private static final int ST = 1 << 7;
    private static final int WALL = 1;
    private static final int BEAN = 2;
    private static final int UNKNOWN = 4;
    private static final int HAS_BEAN_CAN_NOT_EAT = 5;
    private static final int HAS_BEAN_AND_EAT = 6;

    private static int H, W, L, K, R, n;
    private static final int[] key = new int[N * N];
    private static final int[] next = new int[N * N];
    private static final int[] beanX = new int[N];
    private static final int[] beanY = new int[N];
    private static final int[][] beanId = new int[N][N];

    private static final int[][][] beanToAnywhere = new int[8][N][N];

    private static final int[] dirX = new int[]{1, 0, 0, -1};
    private static final int[] dirY = new int[]{0, 1, -1, 0};
    private static final int[][][][] dp = new int[N][N][ST][8];

    private static void init() {
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                for (int st = 0; st < ST; ++st) {
                    for (int k = 0; k <= K; ++k) {
                        dp[i][j][st][k] = Integer.MAX_VALUE;
                    }
                }
            }
        }
        for (int b = 0; b < K; ++b) {
            for (int i = 0; i < N; ++i) {
                for (int j = 0; j < N; ++j) {
                    beanToAnywhere[b][i][j] = Integer.MAX_VALUE;
                }
            }
        }
    }

    private static void initBeanToAnywhere(int x, int y, int[][] dis, int step, int[][] map) {
        if (dis[x][y] <= step) return;
        dis[x][y] = step;
        for (int d = 0; d < 4; ++d) {
            int nx = x + dirX[d];
            int ny = y + dirY[d];
            if (nx <= 0 || ny <= 0 || nx > H || ny > W) continue;
            if (map[nx][ny] == WALL) continue;
            initBeanToAnywhere(nx, ny, dis, step + 1, map);
        }
    }

    private static int encodeBody(List<Integer> bodyX, List<Integer> bodyY) {
        int[] target = new int[L];
        for (int i = 0; i < L - 1; ++i) {
            int x = bodyX.get(i);
            int y = bodyY.get(i);
            int nxtX = bodyX.get(i + 1);
            int nxtY = bodyY.get(i + 1);
            for (int d = 0; d < 4; ++d) {
                if (x + dirX[d] == nxtX && y + dirY[d] == nxtY) {
                    target[i] = d;
                    break;
                }
            }
        }
        int res = 0;
        for (int i = L - 2; i >= 0; --i) {
            res *= 4;
            res += target[i];
        }
        return res;
    }

    private static List<Integer> decode(int begin, int code, int[] dir) {
        List<Integer> res = new ArrayList<>();
        res.add(begin);
        int pre = begin;
        for (int i = 1; i < L; ++i) {
            int d = code % 4;
            code /= 4;
            int now = pre + dir[d];
            res.add(now);
            pre = now;
        }
        return res;
    }

    private static int move(int body, int d) {
        int nBody = body * 4 + (3 - d);
        int stSize = 1 << ((L - 1) * 2);
        return nBody & (stSize - 1);
    }

    private static boolean isBody(int headX, int headY, int x, int y, int code) {
        if (x == headX && y == headY) return true;
        int nowX = headX;
        int nowY = headY;
        for (int i = 1; i < L; ++i) {
            int d = code % 4;
            code /= 4;
            nowX += dirX[d];
            nowY += dirY[d];
            if (x == nowX && y == nowY) return true;
        }
        return false;
    }

    private static void checkEncode(List<Integer> bodyX, List<Integer> bodyY) {
        int code = encodeBody(bodyX, bodyY);
        List<Integer> decodeX = decode(bodyX.get(0), code, dirX);
        List<Integer> decodeY = decode(bodyY.get(0), code, dirY);

        for (int i = 0; i < L; ++i) {
            System.out.println(bodyX.get(i) + " " + bodyY.get(i) + " " + decodeX.get(i) + " " + decodeY.get(i));
//            if(!Objects.equals(decodeX.get(i), bodyX.get(i))) {System.out.println("FUCK");break;}
//            if(!Objects.equals(decodeY.get(i), bodyY.get(i))) {System.out.println("FUCK");break;}
        }
    }

    public static final class Node {
        int x;
        int y;
        int st;
        int step;
        int body;

        int lastBeanId;

        Node(int x, int y, int st, int step, int body, int lastBeanId) {
            this.x = x;
            this.y = y;
            this.st = st;
            this.step = step;
            this.body = body;
            this.lastBeanId = lastBeanId;
        }

        public void show() {
            System.out.print(x + " " + y + " " + showSt(st) + " " + step + " " + showSt(body) + " (");
            List<Integer> nxs = decodeBody(x, body, dirX);
            List<Integer> nys = decodeBody(y, body, dirY);
            for (int i = 0; i < L; ++i) System.out.print(nxs.get(i) + " " + nys.get(i) + ",");
            System.out.println(")");
        }

        public long hash() {
            long res = 0;
            res |= body;

            // step 2800以内
            res <<= 12;
            res |= step;

            // 吃豆状态
            res <<= 7;
            res |= st;

            // 位置 400 以内
            res <<= 9;
            res |= ((long) (x - 1) * W + y - 1);

            res <<= 3;
            res |= lastBeanId;

            return res;
        }

        public static Node decode(long code) {
            int lastBeanId = (int) (code & ((1L << 3) - 1));
            code >>= 3;

            int posCode = (int) (code & ((1L << 9) - 1));
            int x = (posCode / W) + 1;
            int y = (posCode % W) + 1;
            code >>= 9;

            int st = (int) (code & ((1L << 7) - 1));
            code >>= 7;

            int step = (int) (code & ((1L << 12) - 1));
            code >>= 12;

            int body = (int) code;
            return new Node(x, y, st, step, body, lastBeanId);
        }

        public int countSt() {
            int res = 0;
            for (int i = 0; i < 7; ++i) {
                if ((st & (1 << i)) > 0) res++;
            }
            return res;
        }
    }


    private static List<Integer> decodeBody(int begin, int code, int[] dir) {
        List<Integer> res = new ArrayList<>();
        res.add(begin);
        int pre = begin;
        for (int i = 1; i < L; ++i) {
            int d = code % 4;
            code /= 4;
            int now = pre + dir[d];
            res.add(now);
            pre = now;
        }
        return res;
    }


    private static int bfs(int x, int y, int body, int[][] map) {
        Queue<Long> queue = new LinkedList<>();
        Node start = new Node(x, y, 0, 0, body, 0);
        queue.add(start.hash());
        dp[x][y][0][0] = 0;
        int maxSize = 0;
        while (!queue.isEmpty()) {
            maxSize = Math.max(maxSize, queue.size());
            Node top = Node.decode(queue.poll());
            if (top.st == (1 << K) - 1) {
//                System.out.println(maxSize);
                return top.step;
            }
            if (top.step > H * W * (top.countSt() + 1)) continue;
//            if (top.step - 2 > dp[top.x][top.y][top.st]) continue;
//            if (top.x == 3 && top.y == 7)
//                top.show();

            for (int d = 0; d < 4; ++d) {
                int nx = top.x + dirX[d];
                int ny = top.y + dirY[d];
                int nSt = top.st;
                int nLastBeanId = top.lastBeanId;
                if (nx <= 0 || ny <= 0 || nx > H || ny > W) continue;

                int now = map[nx][ny];
                if (now == WALL) continue;
                if (isBody(top.x, top.y, nx, ny, top.body)) continue;

                int eat = UNKNOWN;
                if (now == BEAN) {
                    int id = beanId[nx][ny];
                    for (int i = 0; i < R; ++i) {
                        if (next[i] == id && (((1 << key[i]) & top.st) == 0)) {
                            eat = HAS_BEAN_CAN_NOT_EAT;
                            break;
                        }
                    }
                    if (eat == UNKNOWN) {
                        eat = HAS_BEAN_AND_EAT;
                        nSt = top.st | (1 << id);
                        nLastBeanId = id;
                    }
                }

                if (eat == HAS_BEAN_CAN_NOT_EAT) continue;
                int nStep = top.step + 1;
                boolean go = false;
                if (nStep < dp[nx][ny][nSt][nLastBeanId]) {
                    dp[nx][ny][nSt][nLastBeanId] = nStep;
                    go = true;
                }

                // 吃过豆, 并且上一个豆到当前点距离
                int nowMinDis = dp[nx][ny][nSt][nLastBeanId];
                int preToNowDis = beanToAnywhere[top.lastBeanId][nx][ny];
                if (top.st != 0 &&  nStep < nowMinDis + L && preToNowDis < L) {
                    go = true;
                }

                if (go) {
                    int nBody = move(top.body, d);
                    queue.add(new Node(nx, ny, nSt, nStep, nBody, nLastBeanId).hash());
                }
            }
        }
        return Integer.MAX_VALUE;
    }

    /**
     * 入度判断环
     */
    private static boolean checkCircle() {
        int[] in = new int[K];
        for (int i = 0; i < R; ++i) {
            in[next[i]]++;
        }
        Queue<Integer> root = new LinkedList<>();
        for (int i = 0; i < K; ++i) {
            if (in[i] == 0) root.add(i);
        }
        while (!root.isEmpty()) {
            int nowRoot = root.poll();
            for (int i = 0; i < R; ++i) {
                if (key[i] == nowRoot) {
                    in[next[i]]--;
                    if (in[next[i]] == 0) root.add(next[i]);
                }
            }
        }
        for (int i = 0; i < K; ++i)
            if (in[i] > 0) {
                return true;
            }
        return false;
    }

    /**
     * 判断可达
     */
    private static void checkReach(int x, int y, int[][] map, boolean[][] vis) {
        if (vis[x][y]) return;
        vis[x][y] = true;
        for (int d = 0; d < 4; ++d) {
            int nx = x + dirX[d];
            int ny = y + dirY[d];
            if (nx <= 0 || ny <= 0 || nx > H || ny > W) continue;
            if (map[nx][ny] == WALL) continue;
            checkReach(nx, ny, map, vis);
        }
    }

    private static void solve() {

        H = cin.nextInt();
        W = cin.nextInt();
        L = cin.nextInt();
        K = cin.nextInt();
        R = cin.nextInt();
        n = cin.nextInt();

        int[][] map = new int[21][21];

        List<Integer> bodyX = new ArrayList<>();
        List<Integer> bodyY = new ArrayList<>();

        for (int i = 0; i < L; ++i) {
            int x = cin.nextInt();
            int y = cin.nextInt();
            bodyX.add(x);
            bodyY.add(y);
        }

        for (int i = 0; i < K; ++i) {
            int x = cin.nextInt();
            int y = cin.nextInt();
            beanX[i] = x;
            beanY[i] = y;
            map[x][y] = BEAN;
            beanId[x][y] = i;
        }

        for (int i = 0; i < R; ++i) {
            key[i] = cin.nextInt() - 1;
            next[i] = cin.nextInt() - 1;
        }

        for (int i = 0; i < n; ++i) {
            int x = cin.nextInt();
            int y = cin.nextInt();
            map[x][y] = WALL;
        }

        init();

        if (checkCircle()) {
            System.out.println(-1);
            return;
        }

        // 判断可达
        boolean[][] vis = new boolean[21][21];
        checkReach(bodyX.get(0), bodyY.get(0), map, vis);
        // 预处理豆到地图任一点距离
        for (int i = 0; i < K; ++i) {
            initBeanToAnywhere(beanX[i], beanY[i], beanToAnywhere[i], 0, map);
        }

        for (int i = 0; i < K; ++i) {
            if (!vis[beanX[i]][beanY[i]]) {
                System.out.println(-1);
                return;
            }
        }

        int code = encodeBody(bodyX, bodyY);

        int ans = bfs(bodyX.get(0), bodyY.get(0), code, map);

        System.out.println(ans == Integer.MAX_VALUE ? -1 : ans);
    }


    public static void main(String[] args) {
        int t = cin.nextInt();
        for (int tt = 0; tt < t; ++tt) solve();
    }


    /**
     * 打印状态压缩
     */
    private static String showSt(int st) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < 2 * L; ++i) {
            if ((st & (1 << i)) == 0) res.append("0");
            else res.append("1");
        }
        return res.toString();
    }

    private static class FastReader {
        private InputStream stream;

        private byte[] buf = new byte[1024];

        private int curChar;

        private int numChars;

        FastReader(InputStream stream) {
            this.stream = stream;
        }

        int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        int nextInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public long nextLong() {
            long ret = 0;
            int c = read();
            while (c <= ' ') {
                c = read();
            }
            boolean neg = (c == '-');
            if (neg) {
                c = read();
            }
            do {
                ret = ret * 10 + c - '0';
            } while ((c = read()) >= '0' && c <= '9');
            if (neg) {
                return -ret;
            }
            return ret;
        }

        public String next() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        boolean isSpaceChar(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }
    }
}

===================================================

开悟:我看了他的代码,以下是我的理解——存在一个情况,就是他不是吃到豆子最快,但是因为他的吃豆子蛇的状态,导致他到下一个豆子的后续步数优于最快吃到豆子的状况;所以只要步数不多于最快吃到豆子的最坏情况下,都可以入队进行角逐答案。

  1. 蛇身体之所以可以影响答案,在于蛇身会阻碍蛇某个方向前进,所以如果蛇可以掉头了,他所有方向都可以去了
  2. 理想情况下,蛇只需要走蛇身步数就可以掉头
  3. 为了减少存储,所以只让蛇在豆子蛇身范围内进行掉头,所有其他位置 蛇只要前进就行了。
  4. 这里理想情况下,蛇在最快吃到豆子之后,蛇身步数,离豆子蛇身范围内可以变换成任何形态(存在石头阻碍情况下,无法实现,但是测试数据没有)
  5. 如果你在最优情况外,在豆子附件蛇身范围内,小于蛇身步数,皆可入队,这里就是考虑到其他蛇身状态下,对最终答案的影响。

易错点:这里豆子会成环,导致成环豆子都无法被吃;然后这里为了节省空间,将node的所有东西 压缩到一个longlong里面。

===================================================

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <map>

using namespace std;

const int INF = 0x3f3f3f3f;

int H,W,snakeNum,beanNum,R,stNum;
int snakeHeadX,snakeHeadY,snakeBody;
int ditu[21][21];
int dis[8][21][21];//预处理每个豆子到各点距离
int vis[21][21][1<<7];//三维 坐标+吃豆子状态
int xx[] = {0,1,0,-1};
int yy[] = {1,0,-1,0};

//豆子坐标 + 关键豆子ID
struct bean{
    int x,y,key;
    bean() {}
    bean(int a,int b,int c):x(a),y(b),key(c) {}
}beans[8];


//预处理标记每个豆子蛇身长度范围内可达的点-----------------
struct tmtp{
    int x,y;
    tmtp(int a,int b):x(a),y(b) {};
};
void BFSFB(int b){
    dis[b][beans[b-1].x][beans[b-1].y]=0;
    queue<tmtp> q;
    q.push(tmtp(beans[b-1].x,beans[b-1].y));
    while(!q.empty()){
        tmtp p = q.front();q.pop();
        if(dis[b][p.x][p.y]>=snakeNum) continue;
        for(int i=0;i<4;i++){
            int x = p.x + xx[i] , y = p.y +yy[i];
            if(x<=0||x>H||y<=0||y>W||ditu[x][y]==-2) continue;
            if(dis[b][x][y]<=dis[b][p.x][p.y]+1) continue;
            dis[b][x][y]=dis[b][p.x][p.y]+1;
            q.push(tmtp(x,y));
        }
    }
}

//各种输入和预处理-----------------------------------------
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<<=2;
        if(c-a==0&&d-b==1) snakeBody+=0;
        else if(c-a==1&&d-b==0) snakeBody+=1;
        else if(c-a==0&&d-b==-1) snakeBody+=2;
        else if(c-a==-1&&d-b==0) snakeBody+=3;
        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;beans[i].key=-1;
    }
    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;
    }
    memset(dis,INF,sizeof dis);
    for(int i=0;i<beanNum;i++){
        BFSFB(i+1);
    }
}

//有一个状态压缩函数和一个状态解压函数-----------------------
struct node{
    int x,y,snake,state,lastBeanId,step;
    node(int a,int b,int c,int d,int e,int f):x(a),y(b),snake(c),state(d),lastBeanId(e),step(f) {}
    node(long long tmt){
        lastBeanId = (int) (tmt % (1L<<4));
        tmt = tmt >>4;
        step = (int) (tmt % (1L<<12));
        tmt = tmt >>12;
        state =(int) ( tmt % (1L<<7));
        tmt = tmt>>7;
        snake = (int) (tmt % (1L<<14));
        tmt = tmt>>14;
        y = (int) (tmt % 21),x = (int) (tmt / 21);
    }
    long long makeHash(){
        long long  ans = x*21+y;
        ans <<= 14;ans += snake;
        ans <<= 7;ans += state;
        ans <<= 12;ans+=step;
        ans <<= 4;ans+=lastBeanId;
        return ans;
    }
};

//判断是否碰到蛇身------------------------------------
bool checkXY(int Sx,int Sy,int snake,int tx,int ty){
    int w = (1<<((snakeNum-2)*2));
    int t = snakeNum-1;
    while(t--){
        int tmp = (snake / w)%4;
        Sx+=xx[tmp],Sy+=yy[tmp];
        if(Sx==tx&&Sy==ty) return true;
        w>>=2;
    }
    return false;
}

//答案搜索----------------------------------
int BFS(){
    queue<long long> q;
    memset(vis,INF,sizeof vis);
    vis[snakeHeadX][snakeHeadY][0]=0;
    node start = node(snakeHeadX,snakeHeadY,snakeBody,0,0,0);
    long long tnt = start.makeHash();
    q.push(tnt);

    while(!q.empty()){
        long long pNum = q.front();q.pop();
        node p = node(pNum);

        if((1<<beanNum)-1 == p.state) return p.step;
        if(p.step>H*W*beanNum) continue;

        for(int i=0;i<4;i++){

            int x = p.x + xx[i] , y = p.y +yy[i];
            if(x<=0||x>H||y<=0||y>W||ditu[x][y]==-2) continue;
            if( ditu[x][y] != -1 && beans[ditu[x][y]].key != -1&&((1<<beans[ditu[x][y]].key)&p.state)==0) continue;
            if(checkXY(p.x,p.y,p.snake,x,y)) continue;

            int state = p.state, lastBeanId = p.lastBeanId;
            if(ditu[x][y] != -1&&((1<<ditu[x][y])&p.state)==0) {state |= (1<<ditu[x][y]);lastBeanId = ditu[x][y] +1;}

            bool flag = true;
            //维护步数最优------------------------------
            //这里如果开思维,加上蛇的身体,1<<14-----就会MLE----------------
            if(vis[x][y][state]>p.step+1) {
                vis[x][y][state]=p.step+1;
                flag = false;
            }
            //蛇身体之所以可以影响答案,在于蛇身会阻碍蛇某个方向前进,所以如果蛇可以掉头了,他所有方向都可以去了
            //理想情况下,蛇只需要走蛇身步数就可以掉头
            //为了减少存储,所以只让蛇在豆子蛇身范围内进行掉头
            //这里理想情况下,蛇在最快吃到豆子之后,蛇身步数,离豆子蛇身范围内可以变换成任何形态(存在石头阻碍情况下,无法实现,但是测试数据没有)
            if(state!=0&&(p.step+1<vis[x][y][state]+snakeNum)&&dis[lastBeanId][x][y]<snakeNum) flag = false;
            if(flag) continue;

            int body = p.snake >>2 ;
            if(i==0) body+=(2<<((snakeNum-2)*2));
            else if(i==1) body+=(3<<((snakeNum-2)*2));
            else if(i==2) body+=(0<<((snakeNum-2)*2));
            else body += (1<<((snakeNum-2)*2));

            node tmp = node(x,y,body,state,lastBeanId,p.step+1);
            long long tnt = tmp.makeHash();
            q.push(tnt);
        }
    }
    return -1;
}

//判断豆子是否成环
bool vis2[8];
bool ok(int x){
    vis2[x]=true;
    int key = beans[x].key;
    if(key==-1) return false;
    if(vis2[key]) return true;
    return ok(key);
}

int main()
{
    int _;cin>>_;
    while(_--){
        init();
        bool flag = false;
        for(int i=0;i<beanNum;i++){
            memset(vis2,false,sizeof vis2);
            if(ok(i)) {flag=true;break;}
        }
        if(flag) puts("-1");
        else cout<<BFS()<<endl;
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

盐太郎

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

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

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

打赏作者

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

抵扣说明:

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

余额充值