题目大意:
一个4×4的棋盘上有16枚棋子。每枚棋子都有正反两面,给出棋盘上棋子的初始状态,求将棋盘上棋子全部翻转成正面或者反面的最小步数。翻转规则如下:
当翻转棋子a[i][j]:
a[i-1][j],a[i+1][j],a[i][j-1],a[i][j+1]
也会跟着翻动。根据棋子的不同位置,每翻转一枚棋子都会有2到4枚棋子跟着一起翻转。
棋盘的状态数为2^16。假设将棋子排成一排每种状态都可以用以下公式表示
2^1*x+2^2*x+...+2^16*x(x取0或者1)
按位异或:a^b,a与b不同为1,相同为0.
a^1 当a=1结果为0,a=0时结果1,实现翻转位。
a^0当a=0结果为0,a=1结果为1,保持不变。
假设棋盘当前状态为iCurState;当翻动第1至第16枚棋子时,用以下公式进行转换
pattern[16] = {0xc800,0xe400,0x7200,0x3100,0x8c80,0x4e40,0x2720,0x1310,
0x08c8,0x04e4,0x0272,0x0131,0x008c,0x004e,0x0027,0x0013 };
iCurState^patter[i]
看到这些东西很容易想到BFS,附上代码:
#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
const unsigned short pattern[16] = {0xc800,0xe400,0x7200,0x3100,0x8c80,0x4e40,0x2720,0x1310,
0x08c8,0x04e4,0x0272,0x0131,0x008c,0x004e,0x0027,0x0013 };
queue<int> m_queue;
int input()
{
int m_curstatue = 0;
char ch;
for (int i = 0 ;i < 4 ; i ++)
{
for (int j = 0 ; j < 4; j ++)
{
cin>>ch;
if ('b' == ch)
{
if(0 == i)
m_curstatue += pow(2.0,j);
else
m_curstatue += pow(2.0,i*4 +j);
}
}
}
return m_curstatue;
}
char mark[65536];
int bfs(int m_curstatue)
{
memset(mark,-1,sizeof(mark));
m_queue.push(m_curstatue);
mark[m_curstatue] = 0;
int statue = 0;
while(!m_queue.empty())
{
int tmp_front = m_queue.front();
m_queue.pop();
if (tmp_front == 0 || tmp_front == 65535)
{
return 0;
}
for (int i = 0 ;i < 16 ; i ++)
{
int tmp = tmp_front ^ pattern[i];
if (mark[tmp] == -1)
{
m_queue.push(tmp);
mark[tmp] = mark[tmp_front] + 1;
statue ++;
if (tmp == 65535 || tmp == 0)
{
return mark[tmp];
}
}
}
}
return -1;
}
int main()
{
int m_curstatue = input();
int ans = bfs(m_curstatue);
if (-1 == ans)
cout<<"Impossible"<<endl;
else
cout<<ans<<endl;
return 0;
}
此题还可以用:高斯消元和枚举的方法,将在以后博文发出。