题意:如图所示形状的棋盘上分别有8个1,2,3,要往A~H方向旋转棋盘,使中间8个方格数字相同。如图所示。要求旋转次数最少。如果有多解,操作序列的字典序应尽量小。(本段摘自《算法竞赛入门经典(第2版)》)
分析:
可以使用IDA*来求解。由于每次旋转最多使得一个位置从不正确变为正确,则估价函数为
deep+h()<maxd
,deep为当前深度,maxd为最大限制深度,
h()
为最小的不正确格子数。
代码:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <cstring>
using namespace std;
const int maxn = 25 + 5;
const int line[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}};
const int rev[8] = {5, 4, 7, 6, 1, 0, 3, 2};
const int center[8] = {6, 7, 8, 11, 12, 15, 16, 17};
int a[maxn];
char init[] = {"No moves needed"};
char ans[1005];
bool judge()
{
for (int i = 0; i < 8; ++i)
if (a[center[i]] != a[center[0]])
return false;
return true;
}
int h()
{
int x = 0, y = 0, z = 0;
for (int i = 0; i < 8; ++i)
{
if (a[center[i]] != 1)
++x;
if (a[center[i]] != 2)
++y;
if (a[center[i]] != 3)
++z;
}
return min(min(x, y), z);
}
void move(int x)
{
int tmp = a[line[x][0]];
for (int i = 0; i < 6; ++i)
a[line[x][i]] = a[line[x][i + 1]];
a[line[x][6]] = tmp;
}
bool DFS(int deep, int maxd)
{
if (judge())
{
if (deep)
ans[deep] = '\0';
return true;
}
if (deep + h() > maxd)
return false;
for (int i = 0; i < 8; ++i)
{
ans[deep] = 'A' + i;
move(i);
if (DFS(deep + 1, maxd))
return true;
move(rev[i]);
}
return false;
}
int main()
{
while (~scanf("%d", &a[0]), a[0])
{
memcpy(&ans, &init, sizeof(init));
for (int i = 1; i < 24; ++i)
scanf("%d", &a[i]);
for (int maxd = 0; ; ++maxd)
if (DFS(0, maxd))
{
printf("%s\n%d\n", ans, a[center[0]]);
break;
}
}
return 0;
}