1. 程式人生 > >【BHOJ ModricWang的星靈棋】狀壓 | BFS | 棋類 | trick : [0]陣列 | N

【BHOJ ModricWang的星靈棋】狀壓 | BFS | 棋類 | trick : [0]陣列 | N

ModricWang的星靈棋

核心:狀壓 + BFS (後面還有一個小彩蛋哦)

時間限制: 1000 ms 記憶體限制: 65536 kb

總通過人數: 130 總提交人數: 156

題目描述

ModricWang上次出了兩個題,可是被校園網坑了,所以這一次準備出個一題更比四題強的題來維繫宇宙平衡。

星靈棋是一種流行於Aiur的棋類遊戲。ModricWang經常和他的表哥Artanis下星靈棋。

星靈棋規則如下:在一個4*4的棋盤上擺放了14顆棋子,其中有7顆白色棋子,7顆黑色棋子,有兩個空白地帶,任何一顆黑白棋子都可以向上下左右四個方向移動到相鄰的空格,這叫行棋一步,黑白雙方交替走棋,任意一方可以先走,如果某個時刻使得任意一種顏色的棋子形成四個一線(包括斜線),這樣的狀態為目標棋局,先達到目標棋局者贏。

由於ModricWang還有很多份解題報告沒有批,所以他根本不關心輸贏,只想趕緊把手裡這一盤下完走人。給定當前的棋局狀態,請你幫ModricWang算出最少還要多少步才能下完這盤棋。

輸入

只有一組評測資料。

一個4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地帶用O表示。

保證輸入格式嚴格符合規範,總共只有4個連續的可見行,每行只有4個連續的可見字元。不存在前導空行或前導空格。

保證輸入資料完全符合規則,總共有7個B,7個W和2個O。

輸出

用最少的步數移動到目標棋局的步數。

輸入樣例

BWBO
WBWB
BWBW
WBWO

輸出樣例

5

分析

這道題

AC程式碼

#include <cstdio>
#include <cstring>
#include <unordered_set>
#include <set>

#define GC getchar

enum
{
	BLACK_CHESS = 1,
	BLACK_TURN = 1,
	WHITE_CHESS = 2,
	WHITE_TURN = 2,
	NO_CHESS = 0
};

#define RET  return printf("%d", queue[tail].step), 0

#define UPDATE(r1, c1) \
		if (now.r1>0 && now.getChess(now.r1-1, now.c1) == now.turn) \
		{ \
			queue[tail] = now; \
			--queue[tail].r1, ++queue[tail].step; \
			if (queue[tail].putChess(queue[tail].r1, now.c1, NO_CHESS)) RET; \
			if (queue[tail].putChess(now.r1, now.c1, now.turn)) RET; \
			if (!VIS[now.turn-1].count(*queue[tail].uintValue)) \
			{ \
				VIS[now.turn-1].insert(*queue[tail].uintValue); \
				queue[tail++].turn = 3-now.turn; \
			} \
		} \
		if (now.c1>0 && now.getChess(now.r1, now.c1-1) == now.turn) \
		{ \
			queue[tail] = now; \
			--queue[tail].c1, ++queue[tail].step; \
			if (queue[tail].putChess(now.r1, queue[tail].c1, NO_CHESS)) RET; \
			if (queue[tail].putChess(now.r1, now.c1, now.turn)) RET; \
			if (!VIS[now.turn-1].count(*queue[tail].uintValue)) \
			{ \
				VIS[now.turn-1].insert(*queue[tail].uintValue); \
				queue[tail++].turn = 3-now.turn; \
			} \
		} \
		if (now.r1<3 && now.getChess(now.r1+1, now.c1) == now.turn) \
		{ \
			queue[tail] = now; \
			++queue[tail].r1, ++queue[tail].step; \
			if (queue[tail].putChess(queue[tail].r1, now.c1, NO_CHESS)) RET; \
			if (queue[tail].putChess(now.r1, now.c1, now.turn)) RET; \
			if (!VIS[now.turn-1].count(*queue[tail].uintValue)) \
			{ \
				VIS[now.turn-1].insert(*queue[tail].uintValue); \
				queue[tail++].turn = 3-now.turn; \
			} \
		} \
		if (now.c1<3 && now.getChess(now.r1, now.c1+1) == now.turn) \
		{ \
			queue[tail] = now; \
			++queue[tail].c1, ++queue[tail].step; \
			if (queue[tail].putChess(now.r1, queue[tail].c1, NO_CHESS)) RET; \
			if (queue[tail].putChess(now.r1, now.c1, now.turn)) RET; \
			if (!VIS[now.turn-1].count(*queue[tail].uintValue)) \
			{ \
				VIS[now.turn-1].insert(*queue[tail].uintValue); \
				queue[tail++].turn = 3-now.turn; \
			} \
		}

typedef unsigned char uchar;

std::unordered_set<unsigned int> _[5], *VIS=_+2;
bool WIN[0xaa+1];

struct State
{
	unsigned int uintValue[0];
	uchar row[4], col[4];
	uchar left, right;

	uchar r1, c1;
	uchar r2, c2;
	short step, turn;

	State(void) : step(0), turn(BLACK_TURN) { }

	inline bool putChess(const int r, const int c, uchar p)
	{
		uchar pl2r = p << 2*r, pl2c = p << 2*c,
			  al2r = ~(3 << 2*r), al2c = ~(3 << 2*c);

		row[r] &= al2c, row[r] |= pl2c;
		if (WIN[row[r]]) return true;

		col[c] &= al2r, col[c] |= pl2r;
		if (WIN[col[c]]) return true;

		if (r == c)
		{
			left &= al2c, left |= pl2c;
			if (WIN[left]) return true;
		}

		if (r+c == 3)
		{
			right &= al2r, right |= pl2r;
			if (WIN[right]) return true;
		}

		return false;
	}

	inline uchar getChess(const int r, const int c) const
	{
		return (row[r] >> 2*c) & 3;
	}
};

State queue[666666];

int main()
{
	State src;
	memset(&src, 0, sizeof src);
	WIN[0xaa] = WIN[0x55] = true;

	for (int r=0, first_zero=1; r<4; ++r)
	{
		char str[123];
		scanf("%s", str);
		for (int c=0; c<4; ++c)
		{
			switch(str[c])
			{
				case 'B':
					if (src.putChess(r, c, BLACK_CHESS))
						return puts("0"),0;
					break;
				case 'W':
					if (src.putChess(r, c, WHITE_CHESS))
						return puts("0"),0;
					break;
				case 'O':
					if (first_zero)
						src.r2 = r, src.c2 = c, first_zero = 0;
					else
						src.r1 = r, src.c1 = c;
					break;
			}
		}
	}

	int head = 0, tail = 2;
	*queue = src;
	src.turn = WHITE_TURN;
	queue[1] = src;
	VIS[BLACK_TURN-1].insert(*src.uintValue);	// 黑子的VIS
	VIS[WHITE_TURN-1].insert(*src.uintValue);	// 白子的VIS

	while (head != tail)
	{
		const State &now = queue[head++];
		UPDATE(r1, c1);
		UPDATE(r2, c2);
	}
}

 Trick:關於 [0] 陣列的額外補充