puzzle(1114)欧德里安、燃烧的画、移箱迷宫

目录

欧德里安

燃烧的画

4*4

6*6

8*8(策略)

开局选方向

从最难达成的颜色开始逆推

10*10

12*12

移箱迷宫

初级

策略一:纵向匹配法

策略二:横向匹配法


欧德里安

(2)

 

(3)

(4)

(5)

(9)

  

(18)

  

 (23)

  

燃烧的画

给定初始状态,经过一系列加热和旋转操作,变成目标状态。

加热规则:数字是1则不动,数字x大于1则拆分成1和x-1,把1留在原地,x-1流到下面的一个格子。

4*4

不同的初始态和目标态,也有不同的难度。

4*4里面简单的:

4*4里面复杂的:

6*6

8*8(策略)

开局选方向

在第一次加热之前,我们就需要把方向确定下来。

选方向可以利用的特征很多,比较容易。

比如这一局,我们利用左上角的局部特征:

方向对齐之后是这样:

从最难达成的颜色开始逆推

目前来看,最难达成的是这2个颜色:

这2个颜色基本上要走最短路,不能浪费,所以可以反推出大概的操作方向。

10*10

12*12

这一关是最强大脑原题。

首先确定方向,就是当前方向。

然后选择最难达成的颜色,即下面的棕色和绿色:

所以可选择的方案并不多。

到这一步,最后一行就做完了。

后面就比较简单,策略也还是差不多。

移箱迷宫

规则:

补充规则:

每次滑动,相当于是盒子和四邻居之一交换位置,这个邻居可能是空格也可能是盒子,交换位置之后所有格子往下掉落。

如果形成3个以上,也会一次性全部消除。

如果多个方向同时形成3个或以上,也会一次性全部消除。

消除之后,剩下的盒子会往下掉落,如果形成新的可消除组合,则自动继续消除。

初级

(11)

首先寻找匹配方向:

然后寻找优先消除块:

最后找到操作方法:

(15)

首先寻找匹配方向:

然后寻找优先消除块:

我画了3个黑框,因为左边2个块和上边1个块距离太远,所以在消除这3个块之前还要先把右边的6个块消除掉。

最后找到操作方法:

(23)

 这里的匹配结果是蓝色格子往左移2步,所以操作方法就是:

(24)

(27)

(30)

(33)

(39)

(40)

(42)

首先,红色和深蓝色的水平坐标是OK的,即只要往下掉到同一行即可消除,而黄色是左边的格子需要往右移动一步。

基于此,比较容易想到一个三步的操作方法:

根据这个方案,可以想到一个步骤更少的方案:

(45)

参考下文《递归搜索》

(49)

策略一:纵向匹配法

首先寻找匹配方向,然后寻找优先消除块,最后找到操作方法。

参考初级关卡(11)和(15)

策略二:横向匹配法

如果寻找到的匹配方向是把某个单个的箱子横着移动,则直接得到对应数量的操作内容。

参考初级关卡(23)

策略三:基于移动方格的方案简化

有时我们拿到一个操作数比较多的方案,可以分析我们的整体操作是把哪些方格移动了,移到了什么位置,基于这个操作结果可以反推出一个操作数更少的操作方案。

参考初级关卡(42)

递归搜索

我们把不同的颜色用正整数区分,0表示空格,然后用递归搜索即可搜出一个解。

注意,我们在框定尺寸时,要往左右两边留出空列,而最上面是不需要留出空行的。

代码:

bool down(vector<vector<int>>&v)
{
	bool flag = false;
	int row = v.size(), col = v[0].size();
	for (int j = 0; j < col; j++) {
		int low = 0;
		for (int i = 1; i < row; i++) {
			if (v[low][j] == 0) {
				if (v[i][j])low = i;
			}
			else {
				if (v[i][j])continue;
				for (int k = i; k>low; k--) {
					v[k][j] = v[k - 1][j];
				}
				v[low][j] = 0;
				flag = true;
				low++;
			}
		}
	}
	return flag;
}
bool dels(vector<vector<int>>& v)
{
	int row = v.size(), col = v[0].size();
	vector<vector<bool>>canDel(row, vector<bool>(col, false));
	for (int i = 0; i < row; i++) {
		for (int j = 2; j < col; j++) {
			if (v[i][j] && v[i][j - 2] == v[i][j] && v[i][j - 1] == v[i][j]) {
				canDel[i][j - 2] = canDel[i][j - 1] = canDel[i][j] = true;
			}
		}
	}
	for (int i = 2; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (v[i][j] && v[i-2][j] == v[i][j] && v[i-1][j] == v[i][j]) {
				canDel[i-2][j] = canDel[i-1][j] = canDel[i][j] = true;
			}
		}
	}
	bool flag = false;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (canDel[i][j]) {
				flag = true, v[i][j] = 0;
			}
		}
	}
	return flag;
}
void downAndDel(vector<vector<int>>& v)
{
	while (down(v) || dels(v));
}
bool isFinish(const vector<vector<int>>& v)
{
	int row = v.size(), col = v[0].size();
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (v[i][j]) return false;
		}
	}
	return true;
}

bool search(vector<vector<int>> v, int n)
{
	if (!n)return isFinish(v);
	int row = v.size(), col = v[0].size();
	for (int i = 0; i < row; i++) {
		for (int j = 1; j < col; j++) {
			if (v[i][j] == v[i][j - 1])continue;
			auto v2 = v;
			v[i][j] ^= v[i][j - 1] ^= v[i][j] ^= v[i][j - 1];
			downAndDel(v);
			if (search(v, n-1)) {
				cout << i << " " << j << " " << i << " " << j - 1 << endl;
				return true;
			}
			v = v2;
		}
	}
	for (int i = 1; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (v[i][j] == v[i-1][j])continue;
			auto v2 = v;
			v[i][j] ^= v[i - 1][j] ^= v[i][j] ^= v[i - 1][j];
			downAndDel(v);
			if (search(v, n - 1)) {
				cout << i << " " << j << " " << i-1 << " " << j << endl;
				return true;
			}
			v = v2;
		}
	}
	return false;
}

以初级关卡(45)为例:

所以调用代码就是:

int main()
{
	vector<vector<int>>v{
		{0,1,0,0,0,0},
		{0,2,0,0,0,0},
		{0,3,0,0,0,0},
		{0,2,1,0,0,0},
		{0,1,3,3,0,0},
		{0,2,2,1,0,0},
		{0,1,1,2,2,0}
	};
	search(v, 2);
	return 0;
}

运行结果:

4 1 4 0
3 1 2 1

从下往上,先把(3,1)和(2,1)互换,再把(4,1)和(4,0)互换

即:

策略四:难以分割的相邻同色块

在上面的初级关卡(45)中,其实很容易想到红色的块往左推的操作,但是如果第一步就这样操作就会造成四个黄色块(最下面一行2个,倒数第二行2个)一起消除,从而无解。

我的思路一直是如何分割倒数第二行的2个黄色块,一直没想过可以让这6个黄色块同时消除。

所以策略四就是,如果遇到难以分割的相邻同色块,考虑不分割,而是两组同时消除。

中级

 (2)

这一关的关键是4个黄的要一起消除,4个深蓝的也要一起消除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值