poj 1753 枚举+深搜

题意:给一个4*4的矩阵,每个格非黑即白。选择翻动其中一个格的时候,它的上下左右同时也要翻过去,问最少翻几次可以把矩阵变成一样的颜色。

思路:每个格最多翻动一次(不算翻动其它格时把它也翻过去的情况),这样最多执行同样的操作16次,就可以得出结论。

这是我看到的写得比较清晰的代码,模块功能紧凑,我写了注释。转自:http://blog.sina.com.cn/s/blog_6635898a0100ivzv.html

1753Accepted704K204MSG++1146B

#include <iostream>
using namespace std;

bool map[6][6], findaaa = false;//map存矩阵中格的颜色,findaaa代表是否找到结果
int step;//需要的步数
int dr[5] = {-1, 0, 0, 0, 1};//dr和dc合起来代表每一个格和它周围的格的相对位置
int dc[5] = {0, -1, 0, 1, 0};//分别为上(-1,0)、左(0,1)、中(自己)、右(0,1)、下(1,0)

bool isgoal() {
	for(int i = 1; i<=4; ++i)
	{
		for(int j = 1; j<=4; ++j)
		{
			if(map[i][j] != map[1][1])//判断矩阵中的其它每一格是否都和第一个相同。
			{
				return false;
			}
		}
	}
	return true;
}

void flip(int row, int col)
{
	for(int i=0; i<5; ++i)//map的范围是[6][6]即[0-5][0-5],操作用到的是[1-4][1-4],不会越界
	{
		int r = row + dr[i];
		int c = col + dc[i];
		map[r][c] = !map[r][c];
	}
}

void dfs(int row, int col, int dep)
{
	if(dep == step)
	{
		findaaa = isgoal();
		return;
	}
	if(findaaa || row == 5) return;

	flip(row, col);//给当前点执行flip
	if(col < 4) dfs(row, col + 1, dep + 1);
	else dfs(row + 1, 1, dep + 1);//如果列数超过4就换下一行,就是逐行深搜。

	flip(row, col);//如果没找到,把当前点翻回去,继续找下一个(此时dep就不 + 1了)。
	if(col < 4) dfs(row, col + 1, dep);
	else dfs(row + 1, 1, dep);
}

int main() {
	char c;
	for(int i = 1; i<=4; ++i)
	{
		for (int j = 1; j<=4; ++j)
		{
			cin >> c;
			if(c == 'b') map[i][j] = true;
		}
	}
	for(step = 0; step <= 16; ++step)//最多执行16步,一定能出结果
	{
		dfs(1, 1, 0);//从点(1,1)开始搜,初始化dep为0。
		if(findaaa) break;
	}
	if(findaaa)  cout << step << endl;
	else cout << "Impossible" << endl;
	return 0;
}

这是我看到的比较快捷的代码,用了位操作,我写了注释,转自:http://www.cppblog.com/Yusi-Xiao/archive/2009/03/21/77383.html

1753Accepted936K47MSG++1868B

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int MAX_STATE = 65536;
const int ALL_WHITE_STATE = 0;
const int ALL_BLACK_STATE = 65535;
const int WIDTH_OF_BOARD = 4;
const int SIZE_OF_BOARD = WIDTH_OF_BOARD * WIDTH_OF_BOARD;

int convertPieceColorToInt(char color)//把颜色转换成0、1整型
{
	switch(color)
	{
	case 'b': return 1;
	case 'w': return 0;
	}
}

int flipPiece(int state_id, int position)
{
	state_id ^= (1 << position);//异或操作^,相应位置的1换成0,0换成1.

	if(position -4 >= 0) state_id ^= (1 << (position - 4));//上边的格翻过去
	if(position + 4 < SIZE_OF_BOARD) state_id ^= (1 << (position + 4));//下
	if(position % 4 != 0) state_id ^= (1 << (position - 1));//左
	if(position % 4 != 3) state_id ^= (1 << (position + 1));//右

	return state_id;
}

int main() {
	int current_state_id = 0;
	int state[MAX_STATE];
	queue<int> search_queue;

	memset(state, -1, sizeof(state));

	char color;

	for (int i = 0; i<SIZE_OF_BOARD; ++i)
	{
		cin >> color;
		current_state_id += convertPieceColorToInt(color) << i;//初始值,每一格的状态写到相应位置。
	}

	if(current_state_id == ALL_BLACK_STATE || current_state_id == ALL_WHITE_STATE)
	{
		cout << "0" << endl;
		return 0;
	}

	state[current_state_id] = 0;//用来存需要的步数。
	search_queue.push(current_state_id);

	int next_state_id;

	while(!search_queue.empty())//用队列实现深搜,每次把相邻的点加入队列。
	{
		current_state_id = search_queue.front();
		search_queue.pop();

		for(int i = 0; i<SIZE_OF_BOARD; ++i)
		{
			next_state_id = flipPiece(current_state_id, i);//再翻一个点i,结果为next_state_id。
			if(next_state_id == ALL_WHITE_STATE || next_state_id == ALL_BLACK_STATE)
			{
				cout << state[current_state_id] + 1 << endl;
				return 0;
			}
			if(state[next_state_id] == -1)//如果没有出现过,就入队,值为当前点的值 + 1
			{
				state[next_state_id] = state[current_state_id] + 1;
				search_queue.push(next_state_id);
			}
		}
	}

	cout << "Impossible" << endl;
	return 0;

}


POJ1753题目为"Flip Game",题目给出了一个4x4的棋盘,每个格子有黑色或白色,每次翻转一个格子会同时翻转它上下左右四个格子的颜色,目标是把整个棋盘都变为同一种颜色,求把棋盘变成同种颜色的最小步数。 解题思路: 一般关于棋盘变色的题目,可以考虑使用搜索来解决。对于POJ1753题目,可以使用广度优先搜索(BFS)来解决。 首先,对于每个格子,定义一个状态,0表示当前格子是白色,1表示当前格子是黑色。 然后,我们可以把棋盘抽象成一个长度为16的二进制数,将所有格子的状态按照从左往右,从上往下的顺序排列,就可以用一个16位的二进制数表示整个棋盘的状态。例如,一个棋盘状态为: 0101 1010 0101 1010 则按照从左往右,从上往下的顺序把所有格子的状态连接起来,即可得到该棋盘的状态为"0101101001011010"。 接着,我们可以使用队列来实现广度优先搜索。首先将初始状态加入队列中,然后对于队列中的每一个状态,我们都尝试将棋盘上的每个格子翻转一次,生成一个新状态,将新状态加入队列中。对于每一个新状态,我们也需要记录它是从哪个状态翻转得到的,以便在得到最终状态时能够输出路径。 在搜索过程中,我们需要维护每个状态离初始状态的步数,即将该状态转换为最终状态需要的最小步数。如果我们找到了最终状态,就可以输出答案,即最小步数。 代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值