题目大意:
一条蛇想吃到在(1, 1)的东西,问最短路径;
解题思路:
BFS,很经典的问题,但是这个题有一点不同的就是蛇并不是一个点,而是折现,我们所多进行的操作就是判断蛇的下一步移动是否会吃到自己,能吃到就不行。
因为蛇长度最长为8,我们可以用状态压缩,保存蛇身体相对于后一节的方向,但事实证明,这个方法不如使用数组存储节点来的简单,状态压缩不是很好想。这里介绍的就是状态压缩做法。
利用1<<14的十四个二进制位保存蛇身体的节点,(头部不需要保存,只需要4<<7),每两个二进制位表示0, 1, 2, 3上下左右四个方向。具体操作和解释看代码:
AC代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
//#define maxd 1010
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define mc(x, y) memcpy(x, y, sizeof(x))
#define ms(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define PI pair<int,int>
//#define mapp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int,int > pce;
//#define N 100
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
//const ll INF = (ll)1e18+300;
const int maxd = 11 + 10;
const int maxx = 10100;
using namespace std;
typedef long long ll;
struct snake{
int x, y;
int status, dist;
//snake();
snake(int x = 0, int y = 0, int status = 0, int dist = 0) {
this->x = x;
this->y = y;
this->dist = dist;
this->status = status;
}
};
int dir[10];
int n, m, l;
int dx[] = {1, 0, -1, 0};
int dy[] = {0, -1, 0, 1};
bool maze[maxd][maxd];
bool vis[21][21][1<<14];
bool judge(int x,int y,snake node) {
for(int i = l - 1; i >= 1; i--) {
dir[i] = node.status&3;
node.status >>= 2;
/*
这里就是提取相对方向,这一方向&11(即3)得到方向,并将其右移,提取
下一节方向。
*/
}
int xx = node.x, yy = node.y;
for(int i = 1; i < l; i++) {
xx += dx[dir[i]];
yy += dy[dir[i]];
if(xx==x&&yy==y) return true;
}
return false;
}
int BFS(snake fir) {
if(fir.x == 1 && fir.y == 1) {
return 0;
}
int ans = 0;
queue<snake > que;
que.push(fir);
vis[fir.x][fir.y][fir.status] = true;
while(!que.empty()) {
snake temp = que.front();
que.pop();
if(temp.x == 1 && temp.y == 1) {
return temp.dist;
}
int ans_dis = temp.dist;
for (int i = 0; i < 4; i++) {
int xx = temp.x + dx[i];
int yy = temp.y + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > m || maze[xx][yy]) {
continue;
}
if(judge(xx, yy, temp)) {
continue;
}
int dis = ans_dis + 1;
int sta = (temp.status>>2) + (((i+2)%4) << 2*(l - 2));
/*
这个地方更新了新的身体节点相对位置的状态。
尾部前移(也就是二进制位右移)
更新第二节身体的相对方向并将其存入高位。
因为我们之前保存的时候,是从头部开始,每一节相对于下一节点的方向,
而这里要求的是前移,因此方向是相反的,所以(i+2)%4将方向取反,
这里也就要求我们不能像以前一样随意定义方向数组,1, 3 和 2, 4应当对应相反,
*
*/
if(!vis[xx][yy][sta]) {
snake res;
res.x = xx;
res.dist = dis;
res.y = yy;
res.status = sta;
que.push(res);
vis[xx][yy][sta] = true;
}
}
}
return -1;
}
int main() {
int kase = 0;
while(cin >> n >> m >> l && (m + n + l)) {
ms(vis, false);
kase ++;
snake head;
cin >> head.x >> head.y;
snake temp = head;
for (int i = 0; i < l - 1; i++) {
int x, y;
cin >> x >> y;
for (int ii = 0; ii < 4; ii++) {
if(temp.x + dx[ii] == x && temp.y + dy[ii] == y) {
head.status = (head.status<<2) + ii;
/*
这个地方是高位保存第二节身体,依此类推,最低位保存尾巴。
*/
}
}
temp.x = x;
temp.y = y;
}
ms(maze, false);
int block;
cin >> block;
for (int i = 0; i < block; i++) {
int x, y;
cin >> x >> y;
maze[x][y] = true;
}
cout << "Case " << kase << ": " ;
int ans = BFS(head);
cout << ans << endl;
}
}