OpenJudge NOI 2.1 755:Flip Game

【题目链接】

OpenJudge NOI 2.1 755:Flip Game

【题目翻译】

翻转游戏

描述

翻转游戏是在一个矩形的4*4区域玩的游戏,16个方格中每格都放一个双面棋子。每个棋子的一面是白色另一面是黑色,每个棋子只能是白色面朝上或黑色面朝上。每轮你翻转3到5个棋子,可以把这些棋子朝上一面的颜色从黑色变成白色或反过来。
每轮根据一下规则选择要被翻转的棋子:

  1. 选择16个棋子中的任意一个
  2. 翻转被选中的棋子和所有与它上下左右相邻的棋子(如果存在)

考虑以下位置作为例子:
bwbw
wwww
bbwb
bwwb
这里b表示棋子黑色一面朝上,w表示棋子白色一面朝上。如果我们选择翻转第3行的第1个棋子(这个选择在图中展示了),那么盘面会变成:
bwbw
bwww
wwwb
wwwb
游戏的目标是把所有的棋子翻转成全部棋子白色朝上,或全部棋子黑色朝上。你需要写一个程序来寻找为了能够达到目标需要经过的最少轮数。

输入

输入包含4行,每行有4个字符。每个w和b表示游戏棋盘中的一个位置的棋的颜色。

输出

写入输出文件一个整数:从当前盘面到达成目标需要的最少轮数。如果一开始就达成了目标,写入0。如果不可能达成目标,写入"Impossible"(不包含引号)

样例输入

bwwb
bbwb
bwwb
bwww

样例输出

4

来源

Northeastern Europe 2000

【题目考点】

1. 枚举

2. 二进制

【解题思路】

该题就是点灯问题。
OpenJudge NOI 2.1 1813:熄灯问题思路相同。
枚举对第一行的所有翻转方法。用1表示某处翻转,用0表示某处不翻转,那么对第1行有0000~1111共16种翻转方法。该过程可以通过枚举0~15的整数,而后将其在二进制下进行数位分离,得到对第1行的翻转方案。或者也可以通过搜索来完成。
第一行的翻转方案确定后,第1行的黑白情况就确定了。接下来分两个目标:

  • 目标1:把所有棋子都翻成白色
  • 目标2:把所有棋子都翻成黑色

以目标1为例,想要把所有棋子都翻成白色,第1行的翻转方案已经确定了,第1行翻转后,还剩余一些黑色棋子。那么这些棋子必须通过对第2行同列棋子进行翻转,才能将第1行的黑色棋子都变为白色。因此第2行的翻转方案确定了。
第2行翻转后,第2行的黑色棋子必须通过对第3行的棋子进行翻转后才能变成白色。因此第3行的棋子也确定了。
以此类推,每行的翻转情况都确定了,如果最后一行在翻转后,最后一行不都是白色,那么这一躺对第1行的翻转是无效的,去看下一种对第1行的翻转。
如果最后一行都变为白色,那么得到一个成功翻转的方案,更新成功翻转的最少步数。
而后用同样的方法尝试把所有棋子都翻成黑色,如果成功,则更新成功翻转的最少步数。
最后输出成功翻转的最少步数。

【题解代码】

解法1:枚举

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int dir[4][2] = {
    
    {
    
    0,1},{
    
    0,-1},{
    
    1,0},{
    
    -1,0}};
int mp_ori[5][5], mp[5][5];//mp_ori:原始棋盘, mp:临时棋盘 mp[i][j]为1表示(i,j)是黑色棋子,为0表示(i,j)是白色棋子 mp_t
void flip(int sx, int sy)//在(sx,sy)位置翻转棋
{
    
    
	mp[sx][sy] = !mp[sx][sy];
	for(int i = 0; i < 4; ++i)
	{
    
    
		int x = sx + dir[i][0], y = sy + dir[i][1];
		if(x >= 1 && x <= 4 && y >= 1 && y <= 4)
			mp[x][y] = !mp[x][y];//翻转(x,y)位置的棋子,黑变白,白变黑 
	}
}
//获取把整个盘面都翻转成color颜色需要的翻转次数。第一行的翻转方案由n的二进制数字来表示,如果不能成功翻转,返回-1 
//例:如果n是10,二进制下为1010,那么(1,1), (1,3)位置需要翻转
int getFlipNum(int color, int n)
{
    
    
	memcpy(mp, mp_ori, sizeof(mp_ori));//重置临时棋盘mp为原始棋盘mp_ori
	int a = n, flipNum = 0;//flipNum:翻转棋子的次数 
	for(int j = 4; j >= 1; --j)
	{
    
    //看(1,j)位置是否需要翻转 
		if(a % 2 == 1)//如果这一位是1,则需要翻转
		{
    
    
			flip(1, j);
			flipNum++;
		}
		a /= 2;
	}
	for(int i = 2; i <= 4; ++i)
	{
    
    
		for(int j = 1; j <= 4; ++j)
		{
    
    
			if(mp[i-1][j] != color)//如果(i-1,j)不是目标颜色,那么必须翻转(i,j)
			{
    
    
				flip(i, j);
				flipNum++;
			}
		}
	}
	for(int j = 1; j <= 4; ++j)//如果第4行不都是目标颜色color,那么没有把整个盘面都翻成color 
		if(mp[4][j] != color)
			return -1;
	return flipNum;
}
int main()
{
    
    
	int minFlipNum = INF;//成功把所有棋子都翻成黑色或白色的最少翻转次数
	char c;
	for(int i = 1; i <= 4; ++i)
		for(int j = 1; j <= 4; ++j)
		{
    
    
			cin >> c;
			mp_ori[i][j] = c == 'b' ? 1 : 0;//如果是黑棋子,mp_ori[i][j]是1,如果是白棋子,mp_ori[i][j]是0 
		}
	for(int n = 0; n < 16; ++n)//n变为二进制数字后,就是对第1行的翻转操作,1为 
	{
    
    
		int flipNum_white = getFlipNum(0, n), flipNum_black = getFlipNum(1, n);//将当前棋盘所有棋子翻转成白色、黑色需要的翻转次数 
		if(flipNum_white != -1)
			minFlipNum = min(minFlipNum, flipNum_white);
		if(flipNum_black != -1)
			minFlipNum = min(minFlipNum, flipNum_black);
	}
	if(minFlipNum == INF)
		cout << "Impossible";
	else
		cout << minFlipNum;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/128541880