翻转游戏(flip)
【问题描述】
翻转游戏是在一个 4 格×4 格的长方形上进行的,在长方形的 16 个格上每
个格子都放着一个双面的物件。每个物件的两个面,一面是白色,另一面是黑色,
每个物件要么白色朝上,要么黑色朝上,每一轮你只能翻 3 至 5 个物件,从而由
黑到白的改变这些物件上面的颜色,反之亦然。每一轮被选择翻转的物件遵循以
下规则:
1、从 16 个物件中任选一个。
2、翻转所选择的物件的同时,所有与它相邻的左方物件、右方物件、上方物件
和下方物件(如果有的话),都要跟着翻转。
以下为例:
bwbw
wwww
bbwb
bwwb
这里”b”表示该格子放的物件黑色面朝上、”w”表示该格子放的物件白色朝
上。如果我们选择翻转第三行的第一个物件(如图所示),那么格子状态将变为:
bwbw
bwww
wwwb
wwwb
游戏的目标是翻转到所有的物件白色朝上或黑色朝上。你的任务就是写一个
程序来求最少的翻转次数来实现这一目标。
【输入格式】flip.in
输入文件包含 4 行,每行 4 个字符,每个字符”w” 或 “b”表示游戏开始时格子
上物件的状态。
【输出格式】flip.out
输出文件仅一个整数,即从给定状态到实现这一任务的最少翻转次数。如果给定
的状态就已经实现了目标就输出 0,如果不可能实现目标就输出”Impossible”。
【输入样例】
bwwb
bbwb
bwwb
bwww
【输出样例】
4
【解题思路】
把黑白当作01,把图每一行相接形成一行,转换成01串后化成十进制。
用BFS对每一个点进行翻转,从一个图可以拓展出16种状态,接着继续拓展。
而对每一个点的翻转操作,可以利用位运算中的异或运算。
依照参考程序中的做法,对左上角的翻转操作就是异或19,对右下角的翻转操作就是异或51200。
计算方法就是把要翻转的位置当作是1,别的位置当作是0,按照开头的方法转化成01串后转化成10进制。
【参考程序】
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int num;
string st,sr;
bool visit[65536];
struct data
{
int pic,step;
}que[65536];
int trans(string st)//b当作是1,w当作是0,转换成十进制整数
{
int ans=0,tmp=1;
if (st[0]=='b') ans+=tmp;
for (int i=1;i<=15;i++)
{
tmp=tmp<<1;
if (st[i]=='b')ans+=tmp;
}
return ans;
}
const int table[16]={19,39,78,140,305,626,1252,2248,4880,10016,20032,35968,12544,29184,58368,51200};//打表
int bfs(int num)
{
if (num==0||num==65535)
{
cout<<0;
return 0;
}
int head=0,tail=1;
que[head].pic=num;que[head].step=0;
visit[num]=true;
while (head<tail)
{
for (int i=0;i<16;i++)//拓展
{
que[tail].pic=que[head].pic^table[i];//翻转操作
if (!visit[que[tail].pic])
{
que[tail].step=que[head].step+1;
visit[que[tail].pic]=true;
if (que[tail].pic==0||que[tail].pic==65535)//判断是否到达目标
{
cout<<que[tail].step<<endl;
return 0;
}
tail++;
}
}
head++;
}
return -1;
}
int main()
{
freopen("flip.in","r",stdin);
freopen("flip.out","w",stdout);
cin>>st;
cin>>sr;st=st+sr;
cin>>sr;st=st+sr;
cin>>sr;st=st+sr;//转化成一行
int num=trans(st);
int ans=bfs(num);
if (ans==-1) cout<<"Impossible";
return 0;
}