POJ 1753 Flip Game(枚举+位运算+视频讲解)

题目描述

英文

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值