题目描述
英文
Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it’s black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules:
1.Choose any one of the 16 pieces.
2.Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).
Consider the following position as an example:
bwbw
wwww
bbwb
bwwb
Here “b” denotes pieces lying their black side up and “w” denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become:
bwbw
bwww
wwwb
wwwb
The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal.
输入描述:
The input consists of 4 lines with 4 characters “w” or “b” each that denote game field position.
输出描述:
Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it’s impossible to achieve the goal, then write the word “Impossible” (without quotes).
样例1
输入
bwwb
bbwb
bwwb
bwww
输出
4
中文
给定4 * 4的只有b和w组的。每次把可以把b按成w,或者把w按成b。并且其上下左右也会翻转。
求4*4全部为b或w,翻转的最小次数。可以翻转成功,输出翻转的最小次数,不可以则输出"Impossible"
提交链接
http://poj.org/problem?id=1753
解析
模型转化:b和w可以看成1和0,对于按与不按可以用1和0表示,1表示按,0表示不按。
枚举第一行的按的状态,则第二行按的情况由第一行来决定。依次类推,最后特判最后一行。
地图转化
for(int i=0; i<4; i++)
scanf("%s",mp[i]); //输入的b*w
for(int i=0; i<4; i++)
{
for(int j=0; j<4; j++)
{
if(mp[i][j]=='b')
a[i]|=(1<<j);
else
b[i]|=(1<<j);
}
}
涉及的知识点:位或。二进制的对应位,只要有一个为1就为1。
可以看出,输入的为b*w的地图,a和b数组分别表示的为:
a数组:
1001
1101
1001
1000
b数组:
0110
0010
0110
0111
a数组全部转化为0:全为w
b数组全部转化为0:全为b
我们此时的目标就可以转化为把a和b转化为0,需要的次数哪个少。
枚举第一行所有按的情况
for(int i=0; i<(1<<4); i++)
计算翻转次数
对于第一行,翻转的情况是我们枚举的,对于每一种翻转的情况,我们直接统计就可以,1表示翻转,0表示不翻转。
对于第2~3行,每一行是否需要翻转,是和前一行相关的,我们的目的是把所有的转化为0,所以当前一行出现1的情况,我们在这一行就需要在对应的位置上翻转,把前一行的1变为0
int cal(int x)
{
int ans=0;
while(x)
{
if(x&1)
ans++;
x>>=1;
}
return ans;
}
sum=cal(i); //翻转状态i
sum+=cal(c[j-1]); //前一行的情况
翻转会带动周围翻转
对于第一行,翻转的情况i已知:
c[0]=i^a[0]^(i>>1)^((i<<1)&0xf);
c[1]=i^a[1]; //第一行的翻转影响第2行
c数组表示的这一行的状态,a数组为这一行原本的状态
翻转的时候会带动左右都会反转,所以会有翻转状态i,左移右移的情况
例子:
本身状态 1001 翻转的状态 0110
0111–>0000
c[0]=i^a[0]^(i>>1)^((i<<1)&0xf);
0110^1001 -->1111
1111^((i<<1)&0xf)=1111^1100 --->0011
0011^(i>>1)=0011^0011 --->0000
左移的特殊处理:
((i<<1)&0xf)
...0011110
&
...0001111
=
...0001110
假设不对0xf(1111)位与,i若为1111,此时左移一位,变为…000011110,异或的时候就会多出来1个1
参考代码:
#include <iostream>
#include <stdlib.h>
#include<string.h>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
char mp[5][5];
int c[5];
int a[5],b[5];
int mi;
int cal(int x)
{
int ans=0;
while(x)
{
if(x&1)
ans++;
x>>=1;
}
return ans;
}
void del(int a[])
{
int sum;
memset(c,0,sizeof(c));
for(int i=0; i<(1<<4); i++)
{
sum=cal(i); //翻转状态i
c[0]=i^a[0]^(i>>1)^((i<<1)&0xf);
c[1]=i^a[1]; //第一行的翻转影响第2行
for(int j=1;j<4;j++){
sum+=cal(c[j-1]);
c[j]=c[j]^c[j-1]^(c[j-1]>>1)^((c[j-1]<<1)&0xf);
c[j+1]=c[j-1]^a[j+1];
}
if(c[3]==0)
mi=min(mi,sum);
}
}
int main()
{
mi=INF;
for(int i=0; i<4; i++)
scanf("%s",mp[i]); //输入的b*w
for(int i=0; i<4; i++)
{
for(int j=0; j<4; j++)
{
if(mp[i][j]=='b')
a[i]|=(1<<j);
else
b[i]|=(1<<j);
}
}
del(a);
del(b);
if(mi!=INF)
printf("%d\n",mi);
else
printf("Impossible\n");
return 0;
}
视频
POJ 1753 Flip Game