详细请查看注释!
参考代码:
/*
0 1
2 3
4 5 6 7 8 9 10
11 12
13 14 15 16 17 18 19
20 21
22 23
IDA*算法!
剪枝:反向操作直接跳过,因为反向操作无意义,徒增步数!
估价函数:每次操作会从中间8个数中移出一个数,再移入一个数,所以最多会减少一个不同的数。
因此估价函数可以设为 8 − k ---> k:为中间8个数中出现次数最多的数出现的次数
*/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 24;
// 八个方向下标
int op[8][7] = {
{0, 2, 6, 11, 15, 20, 22},
{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}
};
// 中间八个位置下标
int center[8] = {6, 7, 8, 11, 12, 15, 16, 17};
// 八个方向相反方向下标
int opposite[8] = {5, 4, 7, 6, 1, 0, 3, 2};
char path[100];
int a[N], sum[4];
// 启发式函数(估计函数)
// 中间八个数中出现次数最多的可以不用动,剩下的每个位置都最少需要移动一次
int f(){
// 统计中间八个数出现次数
memset(sum, 0, sizeof sum);
for(int i = 0; i < 8; i ++) sum[a[center[i]]] ++;
// 找到中间八个数出现次数最多的数的出现次数!
int res = 0;
for(int i = 1; i <= 3; i ++) res = max(res, sum[i]);
return 8 - res;
}
// 操作x方向
void operation(int x){
int t = a[op[x][0]];
// 后六个数前移
for(int i = 0; i < 6; i ++) a[op[x][i]] = a[op[x][i + 1]];
a[op[x][6]] = t;
}
bool dfs(int u, int depth, int last){
if(u + f() > depth) return false;
if(f() == 0) return true;
// 枚举八个方向的操作
for(int i = 0; i < 8; i ++){
// 保证前后操作不是对向操作,防止冗余
if(last != opposite[i]){
operation(i);
path[u] = 'A' + i;
if(dfs(u + 1, depth, i)) return true;
operation(opposite[i]);
}
}
return false;
}
int main(){
while(cin >> a[0], a[0]){
for(int i = 1; i < N; i ++) cin >> a[i];
int depth = 0;
while(!dfs(0, depth, -1)) depth ++;
if(!depth) cout << "No moves needed";
for(int i = 0; i < depth; i ++) cout << path[i];
cout << endl << a[6] << endl;
}
return 0;
}