【问题描述】
如下图所示形状的棋盘上分别有8个1,8个2,8个3。在图上标明了8种旋转方式:每种旋转方式包含旋转方向和旋转的是拿一列或行。
(没有图 很尴尬)
现在需要把棋盘经过若干次旋转,使得中间8个方格中的数字相同,例如下图,先进行一次A方式的旋转,再进行一次C方式的旋转后,棋盘中间的8个方格的数字都是2。
现在需要你对给出的棋盘的初始状态和目标状态,需要旋转最少的次数从初始状态变到目标状态,如果有多种旋转方式,输出字典序最小的。
【输入格式】
最多不超过30组数据,每组数据含一行,表示棋盘的初始状态,包含24个整数:1,2,3,对应棋盘从上到下,从左到右的每个格子的数字。输入以0结束。
【输出格式】
每组数据输出2行,第一行字典序最小的旋转操作序列(如果不需要任何操作,请输出“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
【样例解释】
每个测试点不超过30组数据。
【分析】
紫书上爆搜蛮好,这里用A*算法写。
因为题目所求“最少步数”,正好符合DFSID模型。
主程序:
void solve()
{
if(check())
{
printf("No moves needed\n");
printf("%d\n",a[3][3]);
return;
}
for(maxd=0;;maxd++)
{
if(DFSID(1)) break;
}
for(int i=1;i<=maxd;i++)
{
printf("%c",ans[i]);
}
printf("\n%d\n",a[3][3]);
}
//DFSID
//小技巧:恢复A操作可视为F操作,以此类推,可以少写许多函数并节省空间
bool DFSID(int i)
{
if(i>maxd)
{
if(check()) return true;
return false;
}
//估价
//依次处理ABCDEFGH操作,并恢复
//小技巧:恢复A操作可视为F操作,以此类推,可以少写许多函数并节省空间
return false;
估价函数分析:因为每次变换最多只改变一个错误位置,所以在中间8个数中,现在将最大的数视为“正确的”,设有maxd个,那也得至少改变8-maxd次才有可能达到目标状态。
// 估价函数:每次移动最多改变1个错误的位置
int vis[4]={0,0,0,0};
vis[a[3][3]]++;vis[a[3][5]]++;
vis[a[3][4]]++;vis[a[4][5]]++;
vis[a[5][3]]++;vis[a[5][4]]++;
vis[a[5][5]]++;vis[a[4][3]]++;
int h=8-max(vis[1],max(vis[3],vis[2]));//错误的数目
if(i-1+h>maxd) return false;
//前i-1步 和至少还要走h步(估价函数)