POJ 1753

POJ 1753 解法

问题描述:

    翻转棋是在4*4的矩形区域中,每一个方块有一个具有两面的棋子。棋子的一面是白色另一面是黑色,并且每个棋子都只能由一面朝上。每个回合你选择3-5个棋子,然后把棋子翻转改变它的颜色。每个回合翻转的棋子应当遵循以下规则:

    1.选择16个棋子中的任意一个。

    2.翻转所选棋子并翻转它的上下左右相邻的棋子(如果有)。

例如以下的位置:

    bwbw
    wwww
    bbwb
    bwwb

b代表黑色,w代表白色。假设选择第三行的第一个棋子,翻转后得到:

    bwbw
    bwww
    wwwb
    wwwb

游戏的目标是让所有白色朝上或者是黑色朝上。写一个程序找到达到目标的最小回合数。

 

解法:

    这个问题属于递归枚举类的问题,解法有多种,我暂时先将首先有的思路写出来,虽然不算不高效,但在时间范围内还是可行的。

    要得到朝上的颜色都相同,可能选择的方法可以是分别取一个棋子过行翻转,分别取两个棋子进行翻转,分别取三个……分别取16个。在这里要明确的是,每个回合不要同时对同一个棋子进行翻转,原因是翻转偶数次结果是一样的。那么选取的方法就一共有C16取0 + C16取1.……C16取16 = 2的16次方种情况。

    要把2的16次方种情况遍历一遍这是可能的,因此,

    1.定义一个大小为16的数组用来保存16种情况里面选取的情况,同时定义一上最小回合数。

    2.判断是否已经和目标相同,若不相同则进行3,若相同则把该回合数与最小回合数相比较,得到新的最小回合数。

    3.进行递归选取,每得到的一个翻转方法就进行翻转操作,进行2.

    4.输出最小回合数

以下为实现的代码:



#include <iostream>
#include <string.h>
using namespace std;
const int N = 16;

char* pStr = new char[5];
int*  pInt = new int[N];
int   arr[N];
static int min_degree = 100;

bool judge(int arr[N])
{
	int i = 0;
	for(i = N - 1; i > 0; i--)
		if(arr[i] ^ arr[i - 1])
			return false;
	return true;
}

void flip(int *p, int index)
{
	int up		 = index - 4,  down	 = index + 4,
		left	 = index - 1,  right	 = index + 1,
		leftEdge = index / 4 * 4;
	int arr[N];
	memcpy(arr, p, sizeof(int) * N);

	if(index < N && index > -1) 	p[index] =  !p[index];
	else							return;
	if(up > -1)					 p[up]	  =	 !p[up];
	if(down < N)				 p[down]  =  !p[down];
	if(leftEdge <= left)		 p[left]  =  !p[left];
	if(leftEdge + 3 >= right)    p[right] =	 !p[right];
}

int get_degree()
{
	int i = 0, times = 0;
	for(i = 0; i < N; i++)
		if(arr[i])
			times ++;
	return times;
}

void solve()
{
	int i = 0, degree = 0;
	int temp[N];
	memcpy(temp, pInt, sizeof(int) * N);
	for(i = 0; i < N; i++)
	{
		if(arr[i])
		{
			flip(pInt, i);
			if(judge(pInt))
			{
				degree = get_degree();
				min_degree = degree < min_degree ? degree : min_degree;
				break;
			}
		}
	}
	memcpy(pInt, temp, sizeof(int) * N);
}

int get_all(int cur, int last)
{
	int step = 0, resu = 0;
	if(cur > N - 1)
	{
		arr[N - cur + last] = 0;
		return -1;	
	}
	arr[cur] = 1;

	solve();

	for(step = cur; step < N; step++)
	{
		resu = get_all(step + 1, cur);
		if(resu == -1)
			break;
	}
	return 0;
}

int main()
{
	int i = 0, j = 0, resu = 0, count = 0;
	for(j = 0; j < 4; j++)
	{
		cin >> pStr;
		for(i = 0; i < 4; i++, count++)
		{
			if(*(pStr + i) == 'w')
				*(pInt + count) = 1;
			else
				*(pInt + count) = 0;
		}
	}

	if(judge(pInt))
		min_degree = 0;
	else
		get_all(-1, 0);

	if(min_degree < 17)
		cout << min_degree;
	else
		cout << "Impossible" << endl;
	//system("pause");
	return 0;
}


 

 

 

 

 

 

 

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值