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;
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值