题目描述
如下图所示,有一个“#”形的棋盘,上面有1,2,3三种数字各8个。
给定8种操作,分别为图中的A~H。
这些操作会按照图中字母和箭头所指明的方向,把一条长为8的序列循环移动1个单位。
例如下图最左边的“#”形棋盘执行操作A后,会变为下图中间的“#”形棋盘,再执行操作C后会变成下图最右边的“#”形棋盘。
给定一个初始状态,请使用最少的操作次数,使“#”形棋盘最中间的8个格子里的数字相同。
2286_1.jpg
输入格式
输入包含多组测试用例。
每个测试用例占一行,包含24个数字,表示将初始棋盘中的每一个位置的数字,按整体从上到下,同行从左到右的顺序依次列出。
输入样例中的第一个测试用例,对应上图最左边棋盘的初始状态。
当输入只包含一个“0”的行时,表示输入终止。
输出格式
每个测试用例输出占两行。
第一行包含所有移动步骤,每步移动用大写字母“A~G”中的一个表示,字母之间没有空格,如果不需要移动则输出“No moves needed”。
第二行包含一个整数,表示移动完成后,中间8个格子里的数字。
如果有多种方案,则输出字典序最小的解决方案。
输入样例:
1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0
输出样例:
AC
2
DDHH
2
算法 : IDA*
手工打表(算法核心)=。=
代价函数 = 站中间格子最多的那个数,还差多少格子没有站,因为一个移动最多减少一个,所以满足f() <= g()
这题手工打表才是灵魂=。=
#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,12,17,16,15,11};
int oppose[8] = {5,4,7,6,1,0,3,2};
int path[N];
int q[N];
int depth = 0;
int f(){
int sum[4] = {0};
for(int i=0;i<8;i++) sum[q[center[i]]] ++;
int maxv = 0;
for(int i=1;i<=3;i++)
maxv = max(maxv,sum[i]);
return 8 - maxv;
}
void operate(int i)
{
int t = q[op[i][0]];
for(int j=0;j<6;j++)
q[op[i][j]] = q[op[i][j+1]];
q[op[i][6]] = t;
}
bool dfs(int u,int last)
{
if(u + f() > depth) return false;
if(f() == 0) return true;
for(int i=0;i<8;i++)
{
if(oppose[i] == last) continue;
operate(i);
path[u] = i;
if(dfs(u + 1,i)) return true;
operate(oppose[i]);
}
return false;
}
int main(){
while(scanf("%d",&q[0]),q[0])
{
for(int i=1;i<24;i++)
scanf("%d",&q[i]);
depth = 0;
while(!dfs(0,-1)) depth ++;
if(!depth) puts("No moves needed");
else
{
for(int i=0;i<depth;i++)
printf("%c",path[i] + 'A');
puts("");
}
printf("%d\n",q[6]);
}
return 0;
}