目录
一,背景
在第一次自己写代码推导魔方公式(四轴斜转魔方)之后,我决定写一个更通用的推导公式的代码。
输入:魔方的三要素,即部件、操作、目标
输出:操作序列
二,实现思路
1,开放式的分组和编号方案
分组方案需要最基本的魔方基础,比如角块为一组,棱块为一组,中心块为一组。
这些组分别编号第0组,第1组,第2组......具体怎么对应是开放的。
对于每一组,各个块的编号分别为0,1,2......具体怎么对应是开放的。
2,求解目标
求解目标一定是只有一组块交换位置,其他组的所有块位置不变。
三,V1代码(DFS)
为了方便打印路径,我采用了DFS
template<typename T>
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:
map<T, int>m;
int n = 0;
};
template<typename T>
class GetCombineId
{
public:
vector<int> combineId(vector<T>& x)
{
if (v.empty())v.resize(x.size());
vector<int>ans(x.size());
for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
return ans;
}
int id(vector<T>& x)
{
return v2.id(combineId(x));
}
int num()
{
return v2.num();
}
vector<GetSingleId<T>>v;
GetSingleId<vector<int>>v2;
};
struct CubeBlock
{
int typeId;//角块,棱块等,分组id
vector<int>v;//一组块
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<CubeBlock>& b, vector<vector<int>>& 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<int>& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[i] = bv[v[i]];
}
}
void reback(CubeBlock& b, vector<int>& v)
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[v[i]] = bv[i];
}
}
vector<vector<int>>& v;
vector<CubeBlock>& b;
};
class Cube
{
public:
Cube(vector<CubeBlock>& b, vector<CubeOpt>& 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<CubeBlock>&b;
vector<CubeOpt>&opts;
GetCombineId<CubeBlock>m;
};
先以金字塔二重奏魔方为例:
int main()
{
CubeBlock block1(0, 4);//4角块
CubeBlock block2(1, 4);//4棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {0,1,2,3},{1,2,0,3} };
vector<vector<int>>v2 = { {0,1,2,3},{3,0,2,1} };
vector<vector<int>>v3 = { {0,1,2,3},{0,3,1,2} };
vector<vector<int>>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);
vector<CubeOpt>opts = { 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棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {0,3,2,6,4,5,1,7},{2,1,5,3,4,0} };
vector<vector<int>>v2 = { {2,1,5,3,4,0,6,7},{5,1,2,0,4,3} };
vector<vector<int>>v3 = { {0,4,2,1,3,5,6,7},{3,1,2,4,0,5} };
vector<vector<int>>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);
vector<CubeOpt>opts = { 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棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
vector<vector<int>>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
vector<vector<int>>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
vector<vector<int>>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);
vector<CubeOpt>opts = { 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
看看它的效果是啥,我们很快就发明了一个交换三个棱块的公式:
(正着的)0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 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
然而,这还不够,我们需要的是指交换2个块的公式。
四,V1+开放式尝试
注意到上面对于233魔方的尝试,输出结果几乎全都是0和1,所以我们尝试只用0和1两种操作。
int main()
{
CubeBlock block1(0, 8);//8角块
CubeBlock block2(1, 8);//8棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
vector<vector<int>>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
vector<vector<int>>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
vector<vector<int>>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);
vector<CubeOpt>opts = {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获取数据的接口getData,于是把GetSingleId类改成:
template<typename T>
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;
}
};
vector<T>v;
map<Node, int>m;
int n = 0;
};
不知道为什么会报错(后来发现是v只要发生了resize就会出现错误)。
于是我先用一个低效版代替:
template<typename T>
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:
map<T, int>m;
int n = 0;
};
template<typename T>
class GetCombineId
{
public:
vector<int> combineId(vector<T>& x)
{
if (v.empty())v.resize(x.size());
vector<int>ans(x.size());
for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
return ans;
}
int id(vector<T>& x)
{
return v2.id(combineId(x));
}
int num()
{
return v2.num();
}
vector<T> getData(int id)
{
vector<int>ids = v2.getData(id);
vector<T>ans(v.size());
for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
return ans;
}
private:
vector<GetSingleId<T>>v;
GetSingleId<vector<int>>v2;
};
struct CubeBlock
{
int typeId;//角块,棱块等,分组id
vector<int>v;//一组块
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<CubeBlock>& b, vector<vector<int>>& 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<int>& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[i] = bv[v[i]];
}
}
void reback(CubeBlock& b, vector<int>& v)
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[v[i]] = bv[i];
}
}
vector<vector<int>>& v;
vector<CubeBlock>& b;
};
class Cube
{
public:
Cube(vector<CubeBlock>& b, vector<CubeOpt>& opts) :b{ b }, opts{ opts }{}
int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
{
queue<vector<CubeBlock>>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<vector<CubeBlock>> getAns(int id)
{
vector<vector<CubeBlock>>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<CubeBlock>& 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<CubeBlock>&b;
vector<CubeOpt>&opts;
GetCombineId<CubeBlock>m;
map<int, int>fa;
};
还是以金字塔二重奏魔方为例:
int main()
{
CubeBlock block1(0, 4);//4角块
CubeBlock block2(1, 4);//4棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {0,1,2,3},{1,2,0,3} };
vector<vector<int>>v2 = { {0,1,2,3},{3,0,2,1} };
vector<vector<int>>v3 = { {0,1,2,3},{0,3,1,2} };
vector<vector<int>>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);
vector<CubeOpt>opts = { op1,op2,op3,op4 };
Cube cube(b, opts);
int ansId = cube.bfs(1, 4, 4);
vector<vector<CubeBlock>> 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棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {0,3,2,6,4,5,1,7},{2,1,5,3,4,0} };
vector<vector<int>>v2 = { {2,1,5,3,4,0,6,7},{5,1,2,0,4,3} };
vector<vector<int>>v3 = { {0,4,2,1,3,5,6,7},{3,1,2,4,0,5} };
vector<vector<int>>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);
vector<CubeOpt>opts = { op1,op2,op3,op4 };
Cube cube(b, opts);
int ansId = cube.bfs(1, 1, 6);
vector<vector<CubeBlock>> 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棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
vector<vector<int>>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
vector<vector<int>>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
vector<vector<int>>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);
vector<CubeOpt>opts = { op1,op2,op3,op4,op5 };
Cube cube(b, opts);
int ansId = cube.bfs(1, 1, 3);
vector<vector<CubeBlock>> 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个棱块。
六,V3代码(BFS)
为了方便,我把代码改成可以输出多个解的形式,而不是只找到一个解就结束。
template<typename T>
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:
map<T, int>m;
int n = 0;
};
template<typename T>
class GetCombineId
{
public:
vector<int> combineId(vector<T>& x)
{
if (v.empty())v.resize(x.size());
vector<int>ans(x.size());
for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
return ans;
}
int id(vector<T>& x)
{
return v2.id(combineId(x));
}
int num()
{
return v2.num();
}
vector<T> getData(int id)
{
vector<int>ids = v2.getData(id);
vector<T>ans(v.size());
for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
return ans;
}
private:
vector<GetSingleId<T>>v;
GetSingleId<vector<int>>v2;
};
struct CubeBlock
{
int typeId;//角块,棱块等,分组id
vector<int>v;//一组块
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<CubeBlock>& b, vector<vector<int>>& 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<int>& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[i] = bv[v[i]];
}
}
void reback(CubeBlock& b, vector<int>& v)
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[v[i]] = bv[i];
}
}
vector<vector<int>>& v;
vector<CubeBlock>& b;
};
class Cube
{
public:
Cube(vector<CubeBlock>& b, vector<CubeOpt>& opts) :b{ b }, opts{ opts }{}
int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
{
queue<vector<CubeBlock>>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<vector<CubeBlock>> getAns(int id)
{
vector<vector<CubeBlock>>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<vector<CubeBlock>> 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<CubeBlock>& 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<CubeBlock>& b;
vector<CubeOpt>& opts;
GetCombineId<CubeBlock>m;
map<int, int>fa;
};
拿233魔方试一下:
int main()
{
CubeBlock block1(0, 8);//8角块
CubeBlock block2(1, 8);//8棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
vector<vector<int>>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
vector<vector<int>>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
vector<vector<int>>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);
vector<CubeOpt>opts = { op1,op2,op3,op4,op5 };
Cube cube(b, opts);
cube.bfs(1, 2, 2);
return 0;
}
输出:
0 0 1 0 0 1 0 0 1
0 0 2 0 0 2 0 0 2
0 0 1 0 0 2 1 0 0 1 2
0 0 1 0 0 4 1 0 0 1 4
0 0 1 2 0 0 2 1 0 0 2
0 0 2 0 0 1 2 0 0 2 1
0 0 2 0 0 3 2 0 0 2 3
0 0 2 1 0 0 1 2 0 0 1
0 0 3 2 0 0 2 3 0 0 2
0 0 4 1 0 0 1 4 0 0 1
0 1 0 0 2 1 0 0 1 2 0
0 1 0 0 4 1 0 0 1 4 0
0 1 2 0 0 2 1 0 0 2 0
0 2 0 0 1 2 0 0 2 1 0
0 2 0 0 3 2 0 0 2 3 0
0 2 1 0 0 1 2 0 0 1 0
0 3 2 0 0 2 3 0 0 2 0
0 4 1 0 0 1 4 0 0 1 0
七,V3+开放性尝试
看V3的结果,1234四个操作里面都只涉及2个操作,于是我们收缩搜索范围。
int main()
{
CubeBlock block1(0, 8);//8角块
CubeBlock block2(1, 8);//8棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
vector<vector<int>>v1 = { {3,0,1,2,4,5,6,7},{3,0,1,2,4,5,6,7} };
vector<vector<int>>v2 = { {0,6,5,3,4,2,1,7},{0,5,2,3,4,1,6,7} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,6,3,4,5,2,7} };
vector<vector<int>>v4 = { {7,1,2,4,3,5,6,0},{0,1,2,7,4,5,6,3} };
vector<vector<int>>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);
vector<CubeOpt>opts = { op1,op2,op3};
Cube cube(b, opts);
cube.bfs(1, 2, 2);
return 0;
}
在输出的若干解中,选出这一行:
1 0 1 2 0 0 2 1 0 0 2 0 1
这就是交换相邻2个棱块的公式。
如果在ok函数中新增一行
for (int j = 4; j < 8; j++)if (b[i].v[j] != j)return false;
则部分输出被过滤掉,更加方便。
八,V4代码(可视化优化)
基于V3的代码做了微调,使得看起来更容易
template<typename T>
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:
map<T, int>m;
int n = 0;
};
template<typename T>
class GetCombineId
{
public:
vector<int> combineId(vector<T>& x)
{
if (v.empty())v.resize(x.size());
vector<int>ans(x.size());
for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
return ans;
}
int id(vector<T>& x)
{
return v2.id(combineId(x));
}
int num()
{
return v2.num();
}
vector<T> getData(int id)
{
vector<int>ids = v2.getData(id);
vector<T>ans(v.size());
for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
return ans;
}
private:
vector<GetSingleId<T>>v;
GetSingleId<vector<int>>v2;
};
struct CubeBlock
{
int typeId;//角块,棱块等,分组id
vector<int>v;//一组块
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<CubeBlock>& b, vector<vector<int>>& 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<int>& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[i] = bv[v[i]];
}
}
void reback(CubeBlock& b, vector<int>& v)
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[v[i]] = bv[i];
}
}
vector<vector<int>>& v;
vector<CubeBlock>& b;
};
map<int, string>mans;
class Cube
{
public:
Cube(vector<CubeBlock>& b, vector<CubeOpt>& opts) :b{ b }, opts{ opts }{}
int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
{
queue<vector<CubeBlock>>q;
q.push(b);
while (!q.empty()) {
b = q.front();
q.pop();
if (ok(b, targetId, difNumLow, difNumHigh)) {
for (int i = 0; i < b[targetId].v.size(); i++)cout << b[targetId].v[i] << " ";
cout << " ";
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<vector<CubeBlock>> getAns(int id)
{
vector<vector<CubeBlock>>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<vector<CubeBlock>> 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<< mans[j] << " ";
break;
}
if (j == opts.size() - 1)cout << "? ";
}
}
cout << endl;
}
private:
bool ok(vector<CubeBlock>& 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<CubeBlock>& b;
vector<CubeOpt>& opts;
GetCombineId<CubeBlock>m;
map<int, int>fa;
};
以五阶齿轮魔方为例:
int main()
{
CubeBlock block1(0, 8);//8角块
CubeBlock block2(1, 12);//12棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
mans[0] = "上", mans[1] = "下", mans[2] = "前", mans[3] = "后", mans[4] = "左", mans[5] = "右";
vector<vector<int>>v1 = { {2,3,0,1,4,5,6,7},{2,3,0,1,4,5,6,7,8,9,10,11} };
vector<vector<int>>v2 = { {0,1,2,3,6,7,4,5},{0,1,2,3,4,5,6,7,10,11,8,9} };
vector<vector<int>>v3 = { {0,1,7,6,4,5,3,2},{0,1,10,3,4,5,7,6,8,9,2,11} };
vector<vector<int>>v4 = { {5,4,2,3,1,0,6,7},{8,1,2,3,5,4,6,7,0,9,10,11} };
vector<vector<int>>v5 = { {7,1,2,4,3,5,6,0},{0,1,2,11,7,5,6,4,8,9,10,3} };
vector<vector<int>>v6 = { {0,6,5,3,4,2,1,7},{0,9,2,3,4,6,5,7,8,1,10,11} };
CubeOpt op1(b, v1);
CubeOpt op2(b, v2);
CubeOpt op3(b, v3);
CubeOpt op4(b, v4);
CubeOpt op5(b, v5);
CubeOpt op6(b, v6);
vector<CubeOpt>opts = { op1,op2,op3,op4,op5,op6 };
Cube cube(b, opts);
cube.bfs(1, 2, 4);
return 0;
}
在输出的若干行中,很容易就能选出这3行:
0 1 2 3 5 4 7 6 8 9 10 11 0上 1下 2前 0上 1下 3后
0 3 2 1 4 5 7 6 8 9 10 11 0上 2前 0上 2前 0上 2前
0 1 2 3 7 5 4 6 8 9 10 11 0上 2前 0上 4左 0上 2前 0上 4左
九,搜索加速
为了加快搜索效率,我们可以用一个系列操作当成一个原子操作,加入到搜索列表中。
需要注意的是轮换对称性的情况,要区分“轮换对称性意义下的不改变另外一组块”和“不改变另外一组所有块的位置”。
当我们有了一个系列操作,我们可能还是需要知道它对另外一组块的具体作用,用一个小代码跑一下就行:
void test(vector<vector<int>>v, vector<int>id)
{
vector<int>x = v[0];
for (int i = 0; i < x.size(); i++)x[i] = i;
for (auto k : id) {
auto& vi = v[k];
auto tmp = x;
for (int i = 0; i < x.size(); i++)x[i] = tmp[vi[i]];
}
for (int i = 0; i < x.size(); i++)cout << x[i] << ",";
}
参考五阶齿轮魔方
十,V5代码(便于开放性尝试和加速搜索)
为了方便开放性尝试和加速搜索,把代码做了微调
template<typename T>
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:
map<T, int>m;
int n = 0;
};
template<typename T>
class GetCombineId
{
public:
vector<int> combineId(vector<T>& x)
{
if (v.empty())v.resize(x.size());
vector<int>ans(x.size());
for (int i = 0; i < x.size(); i++)ans[i] = v[i].id(x[i]);
return ans;
}
int id(vector<T>& x)
{
return v2.id(combineId(x));
}
int num()
{
return v2.num();
}
vector<T> getData(int id)
{
vector<int>ids = v2.getData(id);
vector<T>ans(v.size());
for (int i = 0; i < v.size(); i++)ans[i] = v[i].getData(ids[i]);
return ans;
}
private:
vector<GetSingleId<T>>v;
GetSingleId<vector<int>>v2;
};
struct CubeBlock
{
int typeId;//角块,棱块等,分组id
vector<int>v;//一组块
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]/4 != i/4)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;
}
};
struct Opt {
vector<vector<int>>v;
string s;
};
class CubeOpt
{
public:
CubeOpt(vector<CubeBlock>& b, Opt& opt) :b{ b }, v{ opt.v }, s{ opt.s }{}//若干组块及其变换
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]);
}
}
string getName()
{
return s;
}
private:
void change(CubeBlock& b, vector<int>& v)//一组块及一个变换,如v[1]=2表示把2号块移到1号块的位置
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[i] = bv[v[i]];
}
}
void reback(CubeBlock& b, vector<int>& v)
{
vector<int>bv = b.v;
for (int i = 0; i < v.size(); i++) {
b.v[v[i]] = bv[i];
}
}
vector<vector<int>>& v;
vector<CubeBlock>& b;
string s;
};
map<int, string>mans;
class Cube
{
public:
Cube(vector<CubeBlock>& b, vector<CubeOpt>& opts) :b{ b }, opts{ opts }{}
int bfs(int targetId, int difNumLow, int difNumHigh)//推导出一个公式
{
queue<vector<CubeBlock>>q;
q.push(b);
int k = 0;
while (!q.empty()) {
k++;
// if (k % 10000 == 0)cout << k << " "<<q.size()<<" ";
b = q.front();
q.pop();
if (ok(b, targetId, difNumLow, difNumHigh)) {
for (auto& bi : b) {
for (int i = 0; i < bi.v.size(); i++)cout << bi.v[i] << ",";
cout << "\n";
}
showAns(m.id(b));
cout << "\n";
}
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)if(q.size()<100000)q.push(b), fa[m.id(b)] = id;
opt.reback();
//if (q.size() % 10000 == 0)cout << q.size()<<" ";
}
}
return 0;
}
vector<vector<CubeBlock>> getAns(int id)
{
vector<vector<CubeBlock>>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<vector<CubeBlock>> 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<< mans[j] << " ";
break;
}
if (j == opts.size() - 1)cout << "? ";
}
}
cout << endl;
}
private:
bool ok(vector<CubeBlock>& 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<CubeBlock>& b;
vector<CubeOpt>& opts;
GetCombineId<CubeBlock>m;
map<int, int>fa;
};
void test(vector<vector<int>>v, vector<int>id)
{
vector<int>x = v[0];
for (int i = 0; i < x.size(); i++)x[i] = i;
for (auto k : id) {
auto& vi = v[k];
auto tmp = x;
for (int i = 0; i < x.size(); i++)x[i] = tmp[vi[i]];
}
for (int i = 0; i < x.size(); i++)cout << x[i] << ",";
}
main函数示例:
int main()
{
CubeBlock block1(0, 24);//24侧边区侧棱
CubeBlock block2(1, 24);//24中心区棱块
vector<CubeBlock>b = vector<CubeBlock>{ block1,block2 };
Opt opt1{ { {4,5,6,7,0,1,2,3,11,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23},
{2,3,0,1,4,5,6,7,20,9,10,11,16,13,14,15,8,17,18,19,12,21,22,23} } ,"上" };
Opt opt2{ { {0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,12,20,21,22,23,16,17,18,19},
{0,1,2,3,6,7,4,5,8,9,18,11,12,13,22,15,16,17,14,19,20,21,10,23} } ,"下" };
Opt opt3{ { {0,1,2,6,21,20,22,7,8,14,13,11,12,10,9,15,16,17,18,3,5,4,19,23} ,
{0,1,17,3,4,5,23,7,10,11,8,9,12,13,14,15,16,6,18,19,20,21,22,2} } ,"前" };
Opt opt4{ { {17,16,18,3,4,5,6,2,15,9,10,12,11,13,14,8,1,0,23,19,20,21,22,7},
{21,1,2,3,19,5,6,7,8,9,10,11,14,15,12,13,16,17,18,0,20,4,22,23} } ,"后" };
Opt opt5{ { {16,1,2,3,4,0,23,22,8,9,15,14,12,13,11,10,21,17,18,19,20,5,7,6},
{0,1,2,13,4,5,6,11,8,9,10,3,12,7,14,15,18,19,16,17,20,21,22,23} } ,"左" };
Opt opt6{ { {0,4,19,18,20,5,6,7,13,12,10,11,9,8,14,15,16,1,3,2,17,21,22,23},
{0,9,2,3,4,15,6,7,8,5,10,11,12,13,14,1,16,17,18,19,22,23,20,21} } ,"右" };
CubeOpt op1(b, opt1);
CubeOpt op2(b, opt2);
CubeOpt op3(b, opt3);
CubeOpt op4(b, opt4);
CubeOpt op5(b, opt5);
CubeOpt op6(b, opt6);
vector<CubeOpt>opts = { op1,op2,op3,op5 };
for (int i = 0; i < opts.size(); i++)mans[i] = opts[i].getName();
Cube cube(b, opts);
cube.bfs(0, 1, 8);
return 0;
}
这样在做开放式尝试时,就只需要修改操作列表opts,不需要修改mans了。
十一,全文总结
1,三阶魔方默认编号
8个角块,上面0-3,下面4-7
12个棱块,上面0-3,中间4-7,下面8-11
2,代码使用总结
只使用最新版的代码即可,除了main函数之外,其他代码对所有魔方都是一样的。
但是,如果想对搜索的公式做进一步限制,可以在ok函数里面加特定条件,这个就是每个魔方独有的代码。(这个其实也可以抽象出来,把差异都放到main函数,懒得弄了)
如果想做开放性尝试,即vector<CubeOpt>opts不取全集,只取一部分,需要修改mans
如果想增加关于块的对称性的信息(如4个一样的块),可以定制化修改changeNum函数。
3,分组总结
基础分组是按照位置可达进行分组。
三阶魔方的层先法,是基于基础分组的更细的分组。
块的对称性也是基于基础分组,如24个块其实可以分成6组,每组4个块都不区分。
4,搜索加速
搜索加速的技巧,参考第9章。