感謝原作者: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;
}
}