感谢原作者:http://wenku.baidu.com/view/273075d733d4b14e852468bb.html 从这里学会的状态压缩
自己写了一遍 用queue记录蛇身, 结果超内存了, 时间就更别说了。而且一开始写的没有考虑到蛇头可以在同一个位置, 身体的状态是可以不同的因素。
关键:1. 蛇只能走4个方向(其实已知是3个), 0, 1, 2, 3表示方向, 只需要2bit, 蛇身最大7, 共14bit即可记录这一节相对于上一节的方向。 要用到位运算。
2. 方向: 谁相对谁不能搞错。
3. 记得清除非记录位为零。剩下的就是一般的BFS 找最短问题了
代码copy上来后, 注释乱了。 自己copy下去 用<C-v> + <=>弄下吧。
#include <stdio.h>
#include <string.h>
#define MAX_N (20 + 1)
#define DIRECTION_N 4
#define STONE 1
#define SPACE 0
#define END_X 1
#define END_Y 1
struct Snake {
int x, y;
int body;
int steps;
};
const int direction[DIRECTION_N][2] = {{0,1}, {-1,0}, {1,0}, {0,-1}}; //顺序对称!!
int row, col, L; //地图行列。蛇长度
int map[MAX_N][MAX_N]; //地图
int visit[MAX_N][MAX_N][1<<14]; //记录地图是否被访问
Snake que[MAX_N*MAX_N*(1<<14)]; //队列
int head, tail; //队头 队尾
int BFS(Snake &);
bool ok(int, int, Snake);
bool visited(Snake &);
int main()
{
Snake holedox;
for (int cases = 1; ; cases++) {
scanf("%d%d%d", &row, &col, &L);
if (!row && !col && !L) {
break;
}
//输入蛇
scanf("%d%d", &holedox.x, &holedox.y); //输入蛇头
holedox.body = 0; //蛇身清零
int now_x = holedox.x, now_y = holedox.y; //now_x, now_y 用于记录当前的坐标
int ent_x, ent_y;
for (int i = 1; i < L; i++) { //snake L-1 bodys
scanf("%d%d", &ent_x, &ent_y);
int sign;
for (sign = 0; sign < DIRECTION_N; sign++) {
if (ent_x == now_x + direction[sign][0]
&& ent_y == now_y + direction[sign][1]) {
break; //找到标号数字就跳出
}
}
holedox.body |= sign << ((i-1)<<1); //第i位标号左移(i-1)*2位 跟body 与操作 记录在body中
now_x = ent_x; //更新当前坐标,准备下次输入
now_y = ent_y;
}
holedox.steps = 0; //初始化步数
//输入石头
memset(map, SPACE, sizeof(map)); //地图清为SPACE
int stones;
scanf("%d", &stones);
for (int i = 0; i < stones; i++) {
scanf("%d%d", &ent_x, &ent_y);
map[ent_x][ent_y] = STONE;
}
//清空访问状态
memset(visit, false, sizeof(visit));
printf("Case %d: %d\n", cases, BFS(holedox));
}
return 0;
}
//广搜
int BFS(Snake &holedox)
{
head = tail = 0; //队列为空
que[tail++] = holedox;
while (head != tail) {
Snake root = que[head++];
if (root.x == END_X && root.y == END_Y) { //有解
return root.steps;
}
if (visited(root)) {
continue;
}
for (int sign = 0; sign < DIRECTION_N; sign++) {
int next_x = root.x, next_y = root.y;
next_x += direction[sign][0];
next_y += direction[sign][1];
if (ok(next_x, next_y, root)) { //如果下一步合法
Snake child = root;
child.steps++; //步数++
child.body <<= 2; //左移2位
child.body &= ~(0xffffffff<<((L-1)<<1));//去除非记录段
child.body |= (3 - sign); //相与 sign加入body的低位.旧头变新身
child.x = next_x; //下一点变成新头
child.y = next_y;
que[tail++] = child; //加入队列
}
}
}
return -1; //完全无解
}
//判断下一步是否合法
bool ok(int next_x, int next_y, Snake root)
{
if (next_x > 0 && next_x <= row && next_y > 0 && next_y <= col) { //是否越界限?
if (map[next_x][next_y] != STONE) { //是否是石头?
int body_x = root.x, body_y = root.y, sign;
for (int i = 1; i < L; i++) { //是否碰到蛇身?
sign = root.body & 3;
root.body >>= 2;
body_x += direction[sign][0];
body_y += direction[sign][1];
if (body_x == next_x && body_y == next_y) {
return false;
}
}
if (next_x == root.x && next_y == root.y) { //是否是蛇头?
return false;
}
return true;
}
}
return false;
}
//判断状态是否被发访问过
bool visited(Snake &root)
{
if (visit[root.x][root.y][root.body] == true) {
return true;
}else {
visit[root.x][root.y][root.body] = true;
return false;
}
}