1753 Flip Game:状态压缩DP翻转4*4期棋盘 0ms通关

题目大意

在这里插入图片描述
进阶题
题目链接
翻转游戏是在一个矩形的4x4场上进行的,在其16个正方形的每个正方形上放置了两个双面棋子。 每一块的一侧为白色,另一侧为黑色,并且每一块都以黑色或白色的一面朝上。 每轮翻转3到5片,从而将其上侧的颜色从黑色更改为白色,反之亦然。 根据以下规则,每轮选择要翻转的棋子:

选择16件中的任何一件。

将选定的片段以及所有相邻片段翻转到选定片段的左侧,右侧,顶部和底部(如果有)。
求最小的翻转次数,既可以反转成全白,也可以全黑。

思路分析

典型的反转问题,和这道题非常类似。看到好像有人用dfs做,肯定也是可以的,毕竟4*4,但是这道进阶题就不行了,反转问题尽量还是状态压缩吧。我们需要对全黑和全白分开计算,先确定第一行的翻转方式,第二行是否反转取决于上一行的对应元素是黑还是白。

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<string.h>
using namespace std;

#define MAX 16
int flip[4][4];//记录每个位置反转了几次
int arr[4][4], res = 10000000;//初始数组,结果值
int dx[5] = { 0,0,0,1,-1 }, dy[5] = { 0,1,-1,0,0 };

int getCnt(int x, int y) {
	//统计(x,y)的上下左右位置一共的翻转次数和本身的情况
	int sum = arr[x][y];
	for (int i = 0; i < 5; i++) {
		int xx = x + dx[i], yy = y + dy[i];
		if (xx >= 0 && xx < 4 && yy >= 0 && yy < 4) sum += flip[xx][yy];
	}
	return sum;
}

int main() {
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			char s; cin >> s;
			if (s == 'b')arr[i][j] = 0;
			else arr[i][j] = 1;
		}
	}
	//1:测试全部反转到0的最小步数:
	for (int i = 0; i < 15; i++) {//第一行的翻转方式:注意不是状态而是如何翻转
		int step = 0;
		memset(flip, 0, sizeof(flip));
		for (int j = 0; j < 4; j++) {//统计第一行的翻转信息
			flip[0][j] = (i >> j) & 1;//i>>j就取出了i的第j位,如果是1表示这里反转一次
			step += flip[0][j];
		}
		for (int m = 1; m < 4; m++) {
			for (int n = 0; n < 4; n++) {
				int sum = getCnt(m - 1, n);//这一行是否翻转取决于上一行的对应元素
				if (sum % 2 == 1) { flip[m][n] += 1; step++; }
			}
		}
		int m = 0;
		for (m = 0; m < 4; m++) {//判断最后一行是否满足要求
			if (getCnt(3, m) % 2 != 0)break;
		}
		if (m == 4) { res = min(res, step); }
	}

	//2:测试全部反转到1的最小步数:
	for (int i = 0; i < 15; i++) {//第一行的翻转方式:注意不是状态而是如何翻转
		int step = 0;
		memset(flip, 0, sizeof(flip));
		for (int j = 0; j < 4; j++) {//统计第一行的翻转信息
			flip[0][j] = (i >> j) & 1;//i>>j就取出了i的第j位,如果是1表示这里反转一次
			step += flip[0][j];
		}
		for (int m = 1; m < 4; m++) {
			for (int n = 0; n < 4; n++) {
				int sum = getCnt(m - 1, n);//这一行是否翻转取决于上一行的对应元素
				if (sum % 2 == 0) { flip[m][n] += 1; step++; }
			}
		}
		int m = 0;
		for (m = 0; m < 4; m++) {//判断最后一行是否满足要求
			if (getCnt(3, m) % 2 == 0)break;
		}
		if (m == 4) { res = min(res, step); }
	}
	if (res != 10000000)cout << res << endl;
	else cout << "Impossible" << endl;
}
发布了186 篇原创文章 · 获赞 13 · 访问量 9320

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105159677