题目链接: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].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;
}
}
}
===================================================
开悟:我看了他的代码,以下是我的理解——存在一个情况,就是他不是吃到豆子最快,但是因为他的吃豆子蛇的状态,导致他到下一个豆子的后续步数优于最快吃到豆子的状况;所以只要步数不多于最快吃到豆子的最坏情况下,都可以入队进行角逐答案。
- 蛇身体之所以可以影响答案,在于蛇身会阻碍蛇某个方向前进,所以如果蛇可以掉头了,他所有方向都可以去了
- 理想情况下,蛇只需要走蛇身步数就可以掉头
- 为了减少存储,所以只让蛇在豆子蛇身范围内进行掉头,所有其他位置 蛇只要前进就行了。
- 这里理想情况下,蛇在最快吃到豆子之后,蛇身步数,离豆子蛇身范围内可以变换成任何形态(存在石头阻碍情况下,无法实现,但是测试数据没有)
- 如果你在最优情况外,在豆子附件蛇身范围内,小于蛇身步数,皆可入队,这里就是考虑到其他蛇身状态下,对最终答案的影响。
易错点:这里豆子会成环,导致成环豆子都无法被吃;然后这里为了节省空间,将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;
}
7万+

被折叠的 条评论
为什么被折叠?



