【记忆化搜索 + 位压缩】POJ 1753 Flip Game

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

这道题目的大致意思就是给定一个4×4的矩阵,其中有白棋和黑棋,最终的目的是要把他们都变成白棋或都变成黑棋,所能进行的操作只有翻转,翻转的条件如下:

  1. 在每次操作的时候可以翻转一个16个棋子中的一个
  2. 每次翻转一个棋子的时候,必须连同其上下左右的棋子一起翻转

问最终,最少经过多少次翻转,可以达到把全部棋变为黑棋或把全部棋子变为白棋的目的?

二、题目思路以及AC代码

这不是我第一次做这道题了,甚至不是第二次。但这回自己思考的很快速,也是思考的比较有梯度的把这道题做了出来。

首先,考虑因为其只有黑棋和白棋两种情况,所以简单起见,我们需要采用位压缩对其进行存储,也就是字母’b’视为二进制1,字母’w’视为二进制0,这样所给的样例就可以存储为二进制1001 1101 1001 1000,则可以直接用一个int来存储。

接下来,考虑如何进行翻转操作。因为我们将4×4的矩阵用一个整数来存储,所以其棋子的翻转,也就是这个整数的某个二进制位的翻转,对于二进制位的翻转,可以采用异或来进行。这样我们就可以提前构建一个长度为16的数组state,当需要翻转棋子i的时候,就可以直接另当前的地图异或state[i]就可以了。

为了更加模块化的写我们的代码,我们还可以把检验当前地图是否满足最终目的(全0或全1)作为一个函数check(),这个函数也就很简单了,直接传入存储地图的整数,如果其值为0或者65535,则满足要求,否则不满足。

然后就是考虑如何翻转棋子了,我这里使用的是dfs,很容易想到,我们只需要对其进行暴力遍历就可以了。答案无非就是0 - 16中的一个(因为如果翻转17次,则某一个棋子会被翻转两次,也就又翻回来了,没有意义),所以我们遍历这16个数,每次尝试各种翻转方式,如果发现最终可以翻转到全0或全1,则输出对应的翻转次数即可。

我一开始也没想那么多,就直接暴力dfs了,然后发现TLE了。我咧嘴一笑,呵呵,直接记忆化搜索!果然,运用了记忆化搜索之后,157ms,Accepted。

下面给出AC代码:

#include <iostream>
using namespace std;

int state[17] = { 0, 51200, 58368, 29184, 12544, 35968, 20032, 10016, 4880, 2248, 1252, 626, 305, 140, 78, 39, 19 };
bool vis[17];

int map;	// 利用位压缩
int dp[65536][17];

void init() {
	for (int i = 0; i < 65536; i++) {
		for (int j = 0; j < 17; j++) {
			dp[i][j] = -1;
		}
	}
}

// 检查是否为全黑或全白  即全1或全0
bool check(int i) {
	if (i == 0 || i == 65535)
		return true;
	return false;
}

// 翻转
int flip(int m, int i) {
	return m ^ state[i];
}

// dfs
// m为当前地图  i为剩余翻转次数
bool dfs(int m, int k) {
	if (k == 0)
		return check(m);
	if (dp[m][k] != -1) return dp[m][k];

	for (int i = 1; i <= 16; i++) {
		if (vis[i]) continue;
		vis[i] = true;
		int tmpm = flip(m, i);
		if (dp[tmpm][k - 1] = dfs(flip(m, i), k - 1))
			return true;
		vis[i] = false;
	}
	return false;
}

int main()
{
	init();

	char c;
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			cin >> c;
			if (c == 'b') {
				map = map | 1;
			}
			map <<= 1;
		}
	}
	map >>= 1;

	int ans = -1;
	for (int i = 0; i <= 16; i++) {
		memset(vis, false, sizeof(vis));
		if (dfs(map, i)) {
			ans = i;
			break;
		}
	}

	if (ans != -1)
		cout << ans << endl;
	else
		cout << "Impossible" << endl;

    return 0;
}

如果有问题,欢迎大家指正!!!

猜你喜欢

转载自blog.csdn.net/m0_38055352/article/details/91900929