POJ1753 FlipGame

分类:状压、暴力枚举

参考博客:

https://www.cnblogs.com/honeycat/p/5176211.html

代码是这位博主原创的,加了点注释

题目:

http://poj.org/problem?id=1753

 

1.

用16位的int型数a来表示棋盘的状态 ,简化问题

2.

用另一个16位int数b表示将要翻转的棋子。用a和b的异或运算表示翻转棋子的操作,异或运算的结果a^b =c表示翻转后的状态。

比如:翻转第9枚棋子

a=1010 0000 1101 1001

b=0000 1000 1100 1000

c=1010 1000 0001 0001

3.

***对于某一个棋子集合的翻转操作,最终的结果与这些棋子翻转的顺序无关,只与集合本身有关。

证明:异或运算符合交换律和结合律

***对于一个棋子,翻转0、2、4...偶数次的结果都一样。翻转1、3、5...奇数次的结果都一样。

证明:b^b =1  a^b^b = a

4.

这样,问题就变成:在一局游戏中,选择一个操作,在这个操作中,对于每一个棋子只有2个选择:翻/不翻。

共有C0/16+C1/16+C2/16+...C16/16种操作。

如果所有的可能操作都不能结束游戏,那么输出impossible。

如果可以结束游戏,选择翻棋子数目最小的一种操作,输出棋子数目。

 

#include 
using namespace std;
const int inf = 0x7fffffff;  //32-bit int的最大值,符号位为0,其他的都是1
char s[4][4];
int cs[16] = { 0x13,0x27,78,140,305,626,1252,2248,4880,8992,20032,35968,12544,29184,58368,51200 };//保存翻第i格子的变化值
int po[16] = { 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768 };//保存2^i
int main()
{
	int value = 0;
	int cmin = inf;
	char c;

	for (int i = 0; i < 16; i++)  //用16位的二进制数,表示状态
	{
		cin >> c;
		if (c == 'b')  //二进制的1代表black 0代表white   第i个数读到b 第i位就置1
			value += (int)po[i];  //  0000 0000 0000 0001  ...
		else
			continue;
	}

	for (int i = 0; i < 65536; i++)
	{//枚举所有状态
		int cou = 0;  //每次循环初始化最小次数
		int cvalue = value;  //初始状态

		for (int j = 0; j < 16; j++)
			if (i & (int)po[j]) //i枚举的是翻转方案的状态,在i的每一个二进制位上1代表翻0代表不翻,此句把这个状态翻译成要在棋盘上所做的操作,  po[j]  的二进制形式,只有第j位为1  一
				//依次检查i的第j位,若是1则执行下面语句,翻转第j个棋子
			{
				cou++;
				cvalue ^= cs[j];  //异或赋值。如: a^=b相当于:a=a^b      
				//cs[j]中  翻动第j个棋子影响的区域在cs[j]的二进制中,表示为1  与1进行异或,cvalue中 1->0  0->1  实现翻转
			}

		// 最终状态与翻牌次序无关,只与待翻牌的集合有关,即:依次翻 4 10 13号牌的结果 与 依次翻13 10 4号牌的结果是相同的!
		// 故只需要枚举所有的待翻牌组合即可, 上面for循环实现此功能

		if (cvalue == 0 || cvalue == 65535)//全黑或全白
			if (cou < cmin)  //若本轮的状态下,所用次数小于当前最小值,
				cmin = cou;
	}

	if (cmin == inf) cout << "Impossible\n";
	else cout << cmin << endl;

	system("pause");
	return 0;
}

 

 

 

你可能感兴趣的:(Algorithm)