康复计划 之 ida*
题意:
这种玩具要你操作最少次数且操作字典序最小,使得中间的8个数字相同。
思路:
迭代加深 启发式 搜索
估价函数采用 8 – 中间数字最多的数字数量
因为每次操作如果想达到最终结果,一次最多只能转化出一个,如 aaab ,一次转化出两个的话,中间那个必定不相同,可以自己在纸上试试。
这道题本身在深搜层数上并不会很大,ida * 的优势就体现了出来。
AC Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
vector<int> ans;
int ary[30], deep;
int center[] = { 7, 8, 9, 12, 13, 16, 17, 18 };
int ret[] = { 5, 4, 7, 6, 1, 0, 3, 2 };
int mat[][8] = {
{ 1, 3, 7, 12, 16, 21, 23 },
{ 2, 4, 9, 13, 18, 22, 24 },
{ 11, 10, 9, 8, 7, 6, 5 },
{ 20, 19, 18, 17, 16, 15, 14 },
{ 24, 22, 18, 13, 9, 4, 2 },
{ 23, 21, 16, 12, 7, 3, 1 },
{ 14, 15, 16, 17, 18, 19, 20 },
{ 5, 6, 7, 8, 9, 10, 11 }
};
bool isEnd()
{
int tmp = ary[7];
for (int i = 1; i < 8; i++)
if (tmp != ary[center[i]])
return false;
return true;
}
void change(int dir)
{
int tmp = ary[mat[dir][0]];
for (int i = 0; i < 6; i++)
ary[mat[dir][i]] = ary[mat[dir][i + 1]];
ary[mat[dir][6]] = tmp;
}
int getH()
{
int num[4] = { 0 };
for (int i = 0; i < 8; i++)
num[ary[center[i]]]++;
return 8 - max(num[1], max(num[2], num[3]));
}
bool dfs(int now)
{
if (now == deep)
return isEnd();
int h = getH();
if (now + h > deep)
return false;
for (int i = 0; i < 8; i++) {
change(i);
ans.push_back(i);
if (dfs(now + 1))
return true;
ans.pop_back();
change(ret[i]);
}
return false;
}
int main()
{
while (scanf("%d", &ary[1]) != EOF && ary[1]) {
for (int i = 2; i <= 24; i++)
scanf("%d", ary + i);
ans.clear();
if (isEnd()) {
puts("No moves needed");
printf("%d\n", ary[7]);
continue;
}
deep = 1;
while (dfs(0) == false)
deep++;
for (unsigned i = 0; i < ans.size(); i++)
printf("%c", ans[i] + 'A');
printf("\n%d\n", ary[7]);
}
return 0;
}