題目大意:
一條蛇想吃到在(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;
}
}