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