Holedox Moving 貪喫蛇 BFS 狀態壓縮

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章