学习内容 Day_1

Posted by n0000000000o's Blog on July 17, 2018

POJ1753 Flip Game

题意简述:

$4×4$ 的棋盘,只有黑白两种颜色的方块,翻动一个棋子后,其周围四个方块也会翻动(四连通)。现给定一个初始状态,求到达全黑或全白的最少翻动次数

分析:

棋盘 $4×4$ ,可以暴力枚举+状态压缩,强行枚举每翻转一次的状态,暴力宽搜求解。总共 $2^{16}(=65536)$ 种情况,每次要考虑 $16$ 种可能性,总的复杂度为 $O( 2^{4^2} * 4^2 )$ ,也就是 $O(2^{n^2} * n^2)$ ,当 $n$ 取 $4$ 时,完全可以考虑。

其实还有其他两种写法,效率都比较高:但是我都不会写

  • 枚举第一排所有情况向后层递推;
  • 将图论转化为数论,利用棋盘上每一点的数学关系(翻转次数的奇偶)建立方程组,高斯消元求解

代码实现

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
using namespace std;
int s;
queue <int> q;
int d[(1<<16) + 10];//用来记步数 
int main()
{
	char c;
	for (int i = 1; i <= 16; i++ )
	{
		cin >> c;
		if(c == 'b') s = (s << 1) + 1;
		else s = (s << 1) + 0;
	}
	q.push(s);
	while(!q.empty())
	{
		int Old = q.front();q.pop();
		int New = Old;		
		if (New == 0 || New == ((1 << 16) - 1))//全黑或全白返回(全为0或1) 
		{
			cout << d[New] <<endl;
			return 0; 
		}	
		// 1 << i 代表第i个点为黑点 ,
		//同时利用 0,1 与 1 异或 改变符号的方法模拟翻转 
		for (int j = 0; j <= 15; j++)
		{
			New = Old;
			New = New ^ (1 << j);//翻转第j点 
			if(j > 3)//不在第一排
				New = New ^ (1 << (j - 4));//可以翻转前一排的点 
			if(j < 12)//不在最后一排
				New = New ^ (1 << (j + 4));//可以翻转后一排的点 
			if(j%4 != 0)//不在第一列 
				New = New ^ (1 << (j - 1));//可以翻转前一列的点 
			if(j%4 != 3) //不在最后一列 
				New = New ^ (1 << (j + 1));//可以翻转后一列的点
			
			if(d[New] == 0)//未经过 
			{
				d[New] = d[Old] + 1;//步数加一 
				q.push(New);
			}
		}
	}
	cout << "Impossible" <<endl;
	return 0;
}