交换公式自动推导

目录

一,背景

二,实现思路

1,开放式的分组和编号方案

2,求解目标

三,V1代码(DFS)

四,开放式尝试


一,背景

在第一次自己写代码推导魔方公式(四轴斜转魔方)之后,我决定写一个更通用的推导公式的代码。

输入:魔方的三要素,即部件、操作、目标

输出:操作序列

二,实现思路

1,开放式的分组和编号方案

分组方案需要最基本的魔方基础,比如角块为一组,棱块为一组,中心块为一组。

这些组分别编号第0组,第1组,第2组......具体怎么对应是开放的。

对于每一组,各个块的编号分别为0,1,2......具体怎么对应是开放的。

2,求解目标

求解目标一定是只有一组块交换位置,其他组的所有块位置不变。

三,V1代码(DFS)

为了方便打印路径,我采用了DFS

template
class GetSingleId
{
public:
	int id(T x)
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		return m[x] = n++;
	}
	int num()
	{
		return n;
	}
private:
	mapm;
	int n = 0;
};
template
class GetCombineId
{
public:
	vector combineId(vector& x)
	{
		if (v.empty())v.resize(x.size());
		vectorans(x.size());
		for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
		return ans;
	}
	int id(vector& x)
	{
		return v2.id(combineId(x));
	}
	int num()
	{
		return v2.num();
	}
	vector>v;
	GetSingleId>v2;
};
struct CubeBlock
{
	int typeId;//角块,棱块等,分组id
	vectorv;//一组块
	CubeBlock(int id, int n)
	{
		typeId = id;
		v.resize(n);
		for (int i = 0; i < n; i++)v[i] = i;//每一组的块都按从0开始编号
	}
	int changeNum()
	{
		int ans = 0;
		for (int i = 0; i < v.size(); i++)if (v[i] != i)ans++;
		return ans;
	}
	bool isOk()
	{
		return changeNum() == 0;
	}
	bool operator<(const CubeBlock& blocks)const
	{
		for (int i = 0; i < v.size() && i < blocks.v.size(); i++) {
			if (v[i] < blocks.v[i])return true;
			if (blocks.v[i] < v[i])return false;
		}
		return false;
	}
};
class CubeOpt
{
public:
	CubeOpt(vector& b, vector>& v) :b{ b }, v{ v }{}//若干组块及其变换
	CubeOpt& operator =(const CubeOpt& opt) {
		b = opt.b, v = opt.v;
		return *this;
	}
	void change()
	{
		for (int i = 0; i < v.size(); i++) {
			change(b[i], v[i]);
		}
	}
	void reback()
	{
		for (int i = 0; i < v.size(); i++) {
			reback(b[i], v[i]);
		}
	}
private:
	void change(CubeBlock& b, vector& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
	{
		vectorbv = b.v;
		for (int i = 0; i < v.size(); i++) {
			b.v[i] = bv[v[i]];
		}
	}
	void reback(CubeBlock& b, vector& v)
	{
		vectorbv = b.v;
		for (int i = 0; i < v.size(); i++) {
			b.v[v[i]] = bv[i];
		}
	}
	vector>& v;
	vector& b;
};

class Cube
{
public:
	Cube(vector& b, vector& opts) :b{ b }, opts{ opts }{}
	bool dfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
	{
		if (m.id(b) != m.num() - 1)return false;
		if (ok(targetId, difNumLow, difNumHigh))return true;
		for (int i = 0; i < opts.size(); i++) {
			auto& opt = opts[i];
			opt.change();
			if (dfs(targetId, difNumLow, difNumHigh)) {
				cout << i << " ";
				return true;
			}
			opt.reback();
		}
		return false;
	}
private:
	bool ok(int targetId, int difNumLow, int difNumHigh)
	{
		for (int i = 0; i < b.size(); i++) {
			int c = b[i].changeNum();
			if (i != targetId) {
				if (c)return false;
			}
			else {
				if (c < difNumLow || c > difNumHigh)return false;
			}

		}
		return true;
	}
	vector&b;
	vector&opts;
	GetCombineIdm;
};

先以金字塔二重奏魔方为例:

int main()
{
	CubeBlock block1(0, 4);//4角块
	CubeBlock block2(1, 4);//4棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {0,1,2,3},{1,2,0,3} };
	vector>v2 = { {0,1,2,3},{3,0,2,1} };
	vector>v3 = { {0,1,2,3},{0,3,1,2} };
	vector>v4 = { {0,1,2,3},{2,1,3,0} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	vectoropts = { op1,op2,op3,op4 };
	Cube(b, opts).dfs(1, 4, 4);
	return 0;
}

输出:

1 0 0(注意,输出的是倒着的,要按照001的顺序执行)

虽然并没有得到上面公式,但是这个结果是对的。

再用四轴旋转魔方试一下:

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 6);//6棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {0,3,2,6,4,5,1,7},{2,1,5,3,4,0} };
	vector>v2 = { {2,1,5,3,4,0,6,7},{5,1,2,0,4,3} };
	vector>v3 = { {0,4,2,1,3,5,6,7},{3,1,2,4,0,5} };
	vector>v4 = { {7,1,0,3,4,5,6,2},{4,1,0,3,2,5} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	vectoropts = { op1,op2,op3,op4 };
	Cube(b, opts).dfs(1, 1, 6);
	return 0;
}

输出1 0 0 1 0 0 1 0 0

这相当于是一个6步的公式,把这个公式重复3遍就得到一个公式,它的效果是把一个四轴旋转魔方的前面和下面中心块交换,后面和右面中心块交换,其他块不变。

再拿233魔方试一下:

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 8);//8棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
	vector>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
	vector>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
	vector>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
	vector>v5 = { {5,4,2,3,1,0,6,7},{4,1,2,3,0,5,6,7} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	CubeOpt op5(b, v5);
	vectoropts = { op1,op2,op3,op4,op5 };
	Cube(b, opts).dfs(1, 1, 4);
	return 0;
}

输出 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0

倒也算是一个公式,但是影响了5个棱块,不便于使用。

于是我们收紧搜索范围:Cube(b, opts).dfs(1, 1, 4);

输出

2 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 2 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0

这显然有点长,但是我们可以提取出

1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0

看看它的效果是啥,我们很快就发明了一个交换三个棱块的公式。

然而,这还不够,我们需要的是指交换2个块的公式。

四,开放式尝试

注意到上面对于233魔方的尝试,输出结果几乎全都是0和1,所以我们尝试只用0和1两种操作。

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 8);//8棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
	vector>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
	vector>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
	vector>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
	vector>v5 = { {5,4,2,3,1,0,6,7},{4,1,2,3,0,5,6,7} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	CubeOpt op5(b, v5);
	vectoropts = {op1,op2};
	Cube(b, opts).dfs(1, 1, 2);
	return 0;
}

输出:

0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0  0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0

公式太长了,为了寻找最短路,我决定改成bfs

五,V2代码(BFS)

BFS需要GetSingleId类新增根据id获取数据的接口。

把GetSingleId类改成:

template
class GetSingleId
{
public:
	int id(T x)
	{
		auto it = m.find(Node{ &x });
		if (it != m.end())return it->second;
		v.push_back(x);
		m[Node{ v.data() + v.size() - 1 }] = n;
		return n++;
	}
	int num()
	{
		return n;
	}
	T* getData(int id)
	{
		return v.data() + id;
	}
private:
	struct Node {
		T* x;
		bool operator<(const Node& nod)const
		{
			return *x < *nod.x;
		}
	};
	vectorv;
	mapm;
	int n = 0;
};

不知道为什么会报错。

于是我先用一个低效版代替:

template
class GetSingleId
{
public:
	int id(T x)
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		return m[x] = n++;
	}
	int num()
	{
		return n;
	}
	T getData(int id)
	{
		for (auto& mi : m)if (mi.second == id)return mi.first;
		return T{};
	}
private:
	mapm;
	int n = 0;
};

template
class GetCombineId
{
public:
	vector combineId(vector& x)
	{
		if (v.empty())v.resize(x.size());
		vectorans(x.size());
		for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
		return ans;
	}
	int id(vector& x)
	{
		return v2.id(combineId(x));
	}
	int num()
	{
		return v2.num();
	}
	vector getData(int id)
	{
		vectorids = v2.getData(id);
		vectorans(v.size());
		for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
		return ans;
	}
private:
	vector>v;
	GetSingleId>v2;
};

struct CubeBlock
{
	int typeId;//角块,棱块等,分组id
	vectorv;//一组块
	CubeBlock() {}
	CubeBlock(int id, int n)
	{
		typeId = id;
		v.resize(n);
		for (int i = 0; i < n; i++)v[i] = i;//每一组的块都按从0开始编号
	}
	int changeNum()
	{
		int ans = 0;
		for (int i = 0; i < v.size(); i++)if (v[i] != i)ans++;
		return ans;
	}
	bool isOk()
	{
		return changeNum() == 0;
	}
	bool operator<(const CubeBlock& blocks)const
	{
		for (int i = 0; i < v.size() && i < blocks.v.size(); i++) {
			if (v[i] < blocks.v[i])return true;
			if (blocks.v[i] < v[i])return false;
		}
		return false;
	}
};

class CubeOpt
{
public:
	CubeOpt(vector& b, vector>& v) :b{ b }, v{ v }{}//若干组块及其变换
	CubeOpt& operator =(const CubeOpt& opt) {
		b = opt.b, v = opt.v;
		return *this;
	}
	void change()
	{
		for (int i = 0; i < v.size(); i++) {
			change(b[i], v[i]);
		}
	}
	void reback()
	{
		for (int i = 0; i < v.size(); i++) {
			reback(b[i], v[i]);
		}
	}
private:
	void change(CubeBlock& b, vector& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
	{
		vectorbv = b.v;
		for (int i = 0; i < v.size(); i++) {
			b.v[i] = bv[v[i]];
		}
	}
	void reback(CubeBlock& b, vector& v)
	{
		vectorbv = b.v;
		for (int i = 0; i < v.size(); i++) {
			b.v[v[i]] = bv[i];
		}
	}
	vector>& v;
	vector& b;
};

class Cube
{
public:
	Cube(vector& b, vector& opts) :b{ b }, opts{ opts }{}
	int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
	{
		queue>q;
		q.push(b);
		while (!q.empty()) {
			b = q.front();
			q.pop();
			if (ok(b, targetId, difNumLow, difNumHigh)) {
				return m.id(b);
			}
			int id = m.id(b);
			for (int i = 0; i < opts.size(); i++) {
				auto& opt = opts[i];
				opt.change();
				if (m.id(b) == m.num() - 1)q.push(b), fa[m.id(b)] = id;
				opt.reback();
			}
		}
		return 0;
	}
	vector> getAns(int id)
	{
		vector>v;
		while(id) {
			v.insert(v.begin(), m.getData(id));
			id = fa[id];
		}
		v.insert(v.begin(), m.getData(id));
		return v;
	}
private:
	bool ok(vector& b, int targetId, int difNumLow, int difNumHigh)
	{
		for (int i = 0; i < b.size(); i++) {
			int c = b[i].changeNum();
			if (i != targetId) {
				if (c)return false;
			}
			else {
				if (c < difNumLow || c > difNumHigh)return false;
			}

		}
		return true;
	}
	vector&b;
	vector&opts;
	GetCombineIdm;
	mapfa;
};

还是以金字塔二重奏魔方为例:

int main()
{
	CubeBlock block1(0, 4);//4角块
	CubeBlock block2(1, 4);//4棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {0,1,2,3},{1,2,0,3} };
	vector>v2 = { {0,1,2,3},{3,0,2,1} };
	vector>v3 = { {0,1,2,3},{0,3,1,2} };
	vector>v4 = { {0,1,2,3},{2,1,3,0} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	vectoropts = { op1,op2,op3,op4 };
	Cube cube(b, opts);
	int ansId = cube.bfs(1, 4, 4);
	vector> v = cube.getAns(ansId);
	for (int i = 1; i < v.size(); i++) {
		for (int j = 0; j < opts.size(); j++) {
			auto v1 = v[i - 1], v2 = v[i];
			b = v1;
			opts[j].change();
			bool same = true;
			for (int k = 0; k < v2.size(); k++)if (v2[k] < b[k] || b[k] < v2[k])same = false;
			if (same) {
				cout << j << " ";
				break;
			}
		}
	}
	return 0;
}

输出0 0 1(这个顺序是正着的

再用四轴旋转魔方试一下:

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 6);//6棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {0,3,2,6,4,5,1,7},{2,1,5,3,4,0} };
	vector>v2 = { {2,1,5,3,4,0,6,7},{5,1,2,0,4,3} };
	vector>v3 = { {0,4,2,1,3,5,6,7},{3,1,2,4,0,5} };
	vector>v4 = { {7,1,0,3,4,5,6,2},{4,1,0,3,2,5} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	vectoropts = { op1,op2,op3,op4 };
	Cube cube(b, opts);
	int ansId = cube.bfs(1, 1, 6);
	vector> v = cube.getAns(ansId);
	for (int i = 1; i < v.size(); i++) {
		for (int j = 0; j < opts.size(); j++) {
			auto v1 = v[i - 1], v2 = v[i];
			b = v1;
			opts[j].change();
			bool same = true;
			for (int k = 0; k < v2.size(); k++)if (v2[k] < b[k] || b[k] < v2[k])same = false;
			if (same) {
				cout << j << " ";
				break;
			}
			if (j == opts.size() - 1)cout << "? ";
		}
	}
	return 0;
}

输出:0 0 1 0 1 1

再拿233魔方试一下:

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 8);//8棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
	vector>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
	vector>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
	vector>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
	vector>v5 = { {5,4,2,3,1,0,6,7},{4,1,2,3,0,5,6,7} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	CubeOpt op5(b, v5);
	vectoropts = { op1,op2,op3,op4,op5 };
	Cube cube(b, opts);
	int ansId = cube.bfs(1, 1, 3);
	vector> v = cube.getAns(ansId);
	for (int i = 1; i < v.size(); i++) {
		for (int j = 0; j < opts.size(); j++) {
			auto v1 = v[i - 1], v2 = v[i];
			b = v1;
			opts[j].change();
			bool same = true;
			for (int k = 0; k < v2.size(); k++)if (v2[k] < b[k] || b[k] < v2[k])same = false;
			if (same) {
				cout << j << " ";
				break;
			}
			if (j == opts.size() - 1)cout << "? ";
		}
	}
	return 0;
}

输出:0 0 1 0 0 1 0 0 1

这个公式的效果是交换顶层不相邻的2个棱块。

计算cube.bfs(1, 3, 3),输出:0 0 1 2 1 0 0 1 2 1

改成:

class Cube
{
public:
	Cube(vector& b, vector& opts) :b{ b }, opts{ opts }{}
	int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
	{
		queue>q;
		q.push(b);
		while (!q.empty()) {
			b = q.front();
			q.pop();
			if (ok(b, targetId, difNumLow, difNumHigh)) {
				showAns(m.id(b));
			}
			int id = m.id(b);
			for (int i = 0; i < opts.size(); i++) {
				auto& opt = opts[i];
				opt.change();
				if (m.id(b) == m.num() - 1)q.push(b), fa[m.id(b)] = id;
				opt.reback();
			}
		}
		return 0;
	}
	vector> getAns(int id)
	{
		vector>v;
		while(id) {
			v.insert(v.begin(), m.getData(id));
			id = fa[id];
		}
		v.insert(v.begin(), m.getData(id));
		return v;
	}
	void showAns(int ansId)
	{
		vector> v = getAns(ansId);
		for (int i = 1; i < v.size(); i++) {
			for (int j = 0; j < opts.size(); j++) {
				auto v1 = v[i - 1], v2 = v[i];
				b = v1;
				opts[j].change();
				bool same = true;
				for (int k = 0; k < v2.size(); k++)if (v2[k] < b[k] || b[k] < v2[k])same = false;
				if (same) {
					cout << j << " ";
					break;
				}
				if (j == opts.size() - 1)cout << "? ";
			}
		}
		cout << endl;
	}
private:
	bool ok(vector& b, int targetId, int difNumLow, int difNumHigh)
	{
		for (int i = 0; i < b.size(); i++) {
			int c = b[i].changeNum();
			if (i != targetId) {
				if (c)return false;
			}
			else {
				if (c < difNumLow || c > difNumHigh)return false;
			}

		}
		return true;
	}
	vector&b;
	vector&opts;
	GetCombineIdm;
	mapfa;
};

int main()
{
	CubeBlock block1(0, 8);//8角块
	CubeBlock block2(1, 8);//8棱块
	vectorb = vector{ block1,block2 };
	vector>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
	vector>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
	vector>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
	vector>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
	vector>v5 = { {5,4,2,3,1,0,6,7},{4,1,2,3,0,5,6,7} };
	CubeOpt op1(b, v1);
	CubeOpt op2(b, v2);
	CubeOpt op3(b, v3);
	CubeOpt op4(b, v4);
	CubeOpt op5(b, v5);
	vectoropts = { op1,op2,op3,op4,op5 };
	Cube cube(b, opts);
	cube.bfs(1, 2, 2);
	return 0;
}

你可能感兴趣的:(算法)