目录
北极星魔方
1,魔方三要素
2,复原方法
(1)复原6个中心块和8个角块的位置
(2)调整24个棱块的位置
(3)调整8个大角块的朝向
北极星魔方
1,魔方三要素
(1)组成部件
6个中心块,8个角块,24个棱块。
每个角块周围有3个棱块,我们把角块+3个棱块成为大角块
(2)可执行操作
大角块旋转120度,有8种。
6个大角块旋转60度,进入斜转状态,有4种
斜转操作,有4种(每种斜转状态下只有1种)
6个大角块旋转60度,恢复正方体状态,有4种(每种斜转状态下只有1种)
(3)目标态
所有块位置和朝向正确。
2,复原方法
(1)复原6个中心块和8个角块的位置
方法一就是像斜转魔方一样,先调整8个角块位置,再调整6个中心块位置。
方法二就是像斜转魔方的计算机求解一样,直接找出操作次数最少的方案。
只需要基于斜转魔方的计算机求解代码,做一点点微小的改动即可得到:
//把6个中心块索引为0上1下 2前3后 4左5右 //上层的左上、右上、左下、右下4个角块,索引是6、8、10、12 //下层的左上、右上、左下、右下4个角块,索引是14、16、18、20 //每个角块跟着一个朝向,复原状态的朝向都是0,即白色黄色都在上面和下面,朝向表示已经顺时针转了几格,3格消除 #define TURN1(a,b,c) {int tmp = v[a];v[a] = v[b], v[b] = v[c], v[c] = tmp; } //3个块位置轮换 #define TURN2(a) {v[a+1]=(v[a+1]+1)%3;} //1个角块旋转 #define TURN3(a,b,c) {TURN1(a,b,c);TURN1(a+1,b+1,c+1);TURN2(a);TURN2(b);TURN2(c);TURN2(a);TURN2(b);TURN2(c);} //3个角块位置轮换+朝向改变 //以下是4个顺时针旋转操作 vector<int> f1(vector<int>& input) { vector<int> v = input; TURN1(2, 1, 5); TURN2(20); TURN3(12, 18, 16); return v; } vector<int> f2(vector<int>& input) { vector<int> v = input; TURN1(2, 4, 1); TURN2(18); TURN3(10, 14, 20); return v; } vector<int> f3(vector<int>& input) { vector<int> v = input; TURN1(4, 3, 1); TURN2(14); TURN3(6, 16, 18); return v; } vector<int> f4(vector<int>& input) { vector<int> v = input; TURN1(5, 1, 3); TURN2(16); TURN3(8, 20, 14); return v; } vector<std::function<vector<int>(vector<int>&)>> vf{ f1,f2,f3,f4 }; void bfs(vector<int>& target) { vector<int>v{ 1,2,3,4,5,6,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0 }; auto v0 = v; queue<vector<int>>q; q.push(v); map<vector<int>, int>s; s[v] = 0; map<vector<int>, vector<int>>fa; map<vector<int>, int>faOpt; faOpt[v] = -1; while (!q.empty()) { auto v = q.front(); q.pop(); for (int i = 0; i < 4; i++) { auto v2 = vf[i](v); if (s.find(v2) == s.end()) { q.push(v2); s[v2] = s[v] + 1; //if (s.size() % 100000 == 0)cout << q.size() << " " << s.size() << endl; fa[v2] = v; faOpt[v2] = i; bool flag = true; for (int i = 0; i < v0.size(); i++) { if (v0[i] > 0 && v2[i] != target[i])flag = false; } if (flag) { vector<string> strs{ "右下","左下","左上","右上" }; int lineId = 0; while (faOpt[v2] >= 0) { cout << ++lineId << " 以底层的" << strs[faOpt[v2]] << "的大角块为轴逆时针旋转" << endl; v2 = fa[v2]; } return; } } } } cout << s.size(); } //以下是4个顺时针旋转操作 vector<int> f5(vector<int>& input) { vector<int> v = input; TURN1(2, 1, 5); return v; } vector<int> f6(vector<int>& input) { vector<int> v = input; TURN1(2, 4, 1); return v; } vector<int> f7(vector<int>& input) { vector<int> v = input; TURN1(4, 3, 1); return v; } vector<int> f8(vector<int>& input) { vector<int> v = input; TURN1(5, 1, 3); return v; } vector<std::function<vector<int>(vector<int>&)>> vf2{ f5,f6,f7,f8 }; //奇偶性检查,即魔方整体要不要旋转90度 bool checkNeedTurn(vector<int>& target) { vector<int>v{ 1,2,3,4,5,6 }; queue<vector<int>>q; q.push(v); map<vector<int>, int>s; s[v] = 0; while (!q.empty()) { auto v = q.front(); q.pop(); for (int i = 0; i < 4; i++) { auto v2 = vf2[i](v); if (s.find(v2) == s.end()) { q.push(v2); s[v2] = s[v] + 1; } } } if (s.find(target) != s.end()) { return false; } //篡改数据,把魔方整体往左旋转90度,保持白色放在顶面,也就是前面转到左边,左边转到后面 int tmp = target[2]; target[2] = target[5], target[5] = target[3]; target[3] = target[4], target[4] = tmp; return true; } int main() { vector<int>v{ 1,2,3,4,5,6 }; cout << "分别输入白色 黄色 红色 橙色 绿色 蓝色中心块的位置,用数字表示,0上1下 2前3后 4左5右" << endl; int id, t; for (int i = 1; i <= 6; i++) { cin >> id; v[id] = i; } if (v[0] != 1) { cout << "把白色放在顶面,重新运行程序"; return 0; } if (checkNeedTurn(v)) { cout << "把魔方整体往左旋转90度,保持白色放在顶面,也就是前面转到左边,左边转到后面,然后继续" << endl; } for (int i = 1; i <= 16; i++) { v.push_back(0); } cout << "分别输入 白绿橙 白蓝橙 白绿红 白蓝红 黄绿橙 黄蓝橙 黄绿红 黄蓝红 8个大角块的位置和朝向" << endl; cout << "位置用数字表示,上层的左上、右上、左下、右下分别是6、8、10、12,下层的左上、右上、左下、右下分别是14、16、18、20" << endl; for (int i = 1; i <= 8; i++) { cin >> id; v[id] = i; } bfs(v); return 0; }运行示例:
(2)调整24个棱块的位置
4个大角块里面的12个棱块可以交换任意2个的位置,另外12个也可以交换任意2个。
两组之间是不可能交换位置的,即棱块的位置奇偶性其实是固定的。
也就是说,12个棱块最多交换11次,另外12个棱块也是最多交换11次,就全部调整完成,不存在复杂的情况。
这一步太简单了,不需要公式。
(3)调整8个大角块的朝向
每个大角块都有单独旋转。
这一步太简单了,不需要公式。