显然直接枚举全排列数据太大了,而且有很多不必要的操作。这是一道明显的状态搜索题,那么该采用什么方法呢? bfs?迭代bfs?A*?还是双向bfs
这题用双向bfs和a都可以做出来,但是相比之下,A会比双向bfs快一点。
既然选择了a*就要考虑如何找启发式函数了,经过分析,每次经过一次旋转的话对于一个状态的改变量只有1个(不是3个!一开始自己以为是3个状态改变就错了),因为这个是与顺序无关的,比如 4 2 3 1 5 对于中间3个的状态开始时2 3 1,向右旋转一次 x(4前面的一个数,没写出来)4 2 3 5,此时中间的状态就是4 2 3,又因为和顺序没关系,就是多了一个4进来而已,也就改变了一个而已。
还有一个就是一开始想估计maxd,最深 有多少,但是自己能力有限也没有想出来,只好用迭代加深搜索法了(某种意义上,迭代可能还更好),代码如下。
最后就是学会这道题隐式图的建立方法,不要用7×7的二维数组建立,太浪费空间了,操作也会变得麻烦一些。
总结:scanf("%d "…) 这是错的,别随便加空格啊,卡了半天。
以及 a='a ’ 这种,也别随便加空格。这种小细节注意,不然卡死人。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int line[8][7] = { //8个方向,每个方向7个数的编号。
{ 0, 2, 6,11,15,20,22}, // A-H
{ 1, 3, 8,12,17,21,23},
{10, 9, 8, 7, 6, 5, 4},
{19,18,17,16,15,14,13},
{23,21,17,12,8,3,1},
{22,20,15,11,6,2,0},
{13,14,15,16,17,18,19},
{4,5,6,7,8,9,10},
};
const int rev[8] = { 5,4,7,6,1,0,3,2 }; //上面数组每行对应的一行,比如a对应f,a在0,f在5
const int center[8] = { 6, 7, 8, 11, 12, 15, 16, 17 };
char mirror[8]={'F','E','H','G','B','A','D','C'};
int a[24]; //存每个编号的数字
char ans[1000];
bool is_final1() {
for (int i = 0; i < 8; ++i) {
if (a[center[0]] != a[center[i]]) {
return false;
}
}
return true;
}
int diff(int k) { //就是看中间8个有多少与k不同,k取1或者2或者3
int ans = 0;
for (int i = 0; i < 8; ++i) {
if (k != a[center[i]]) {
++ans;
}
}
return ans;
}
int h() {
return min(min(diff(1), diff(2)), diff(3));
}
void move(int i) {
int tmp = a[line[i][0]];
for (int j = 0; j < 6; ++j) {
a[line[i][j]] = a[line[i][j + 1]];
}
a[line[i][6]] = tmp;
}
bool dfs(int d,int maxd) {
if (is_final1()) {
ans[d] = '\0';
printf("%s\n", ans);
return true;
}
if (h() > 1 * (maxd - d)) return false;
for (int i = 0; i < 8; ++i) {
ans[d] = 'A' + i;
if(d>0&&ans[d-1]==mirror[i]) continue; //提速。
move(i);
if (dfs(d + 1, maxd)) return true;
move(rev[i]); //回溯
}
return false;
}
int main() {
while (scanf("%d", &a[0]) == 1 && a[0]) {
for (int i = 1; i < 24; ++i) scanf("%d", &a[i]);
if (is_final1()) {
printf("No moves needed\n");
}
else {
for (int i = 1;; ++i) {
if (dfs(0, i)) break;
}
}
printf("%d\n", a[6]);
}
return 0;
}