【题目来源】
https://www.luogu.com.cn/problem/UVA1343
https://www.acwing.com/problem/content/description/183/
【题目描述】
如下图所示,有一个 # 形的棋盘,上面有 1,2,3 三种数字各 8 个。
给定 8 种操作,分别为图中的 A∼H。
这些操作会按照图中字母和箭头所指明的方向,把一条长为 7 的序列循环移动 1 个单位。
例如下图最左边的 # 形棋盘执行操作 A 后,会变为下图中间的 # 形棋盘,再执行操作 C 后会变成下图最右边的 # 形棋盘。
给定一个初始状态,请使用最少的操作次数,使 # 形棋盘最中间的 8 个格子里的数字相同。
【输入格式】
输入包含多组测试用例。
每个测试用例占一行,包含 24 个数字,表示将初始棋盘中的每一个位置的数字,按整体从上到下,同行从左到右的顺序依次列出。
输入样例中的第一个测试用例,对应上图最左边棋盘的初始状态。
当输入只包含一个 0 的行时,表示输入终止。
【输出格式】
每个测试用例输出占两行。
第一行包含所有移动步骤,每步移动用大写字母 A∼H 中的一个表示,字母之间没有空格,如果不需要移动则输出 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
【算法分析】
● 打表:棋盘是 # 形,为了方标取数和操作,可给这些位置按下图所示依次编号为0~23,同时给A~G这8个操作依次编号为0~7。
● 估价函数:因为每次操作只会改变中心8个方格里的一个数,而题目要求中心8个方格的数都相同,故最小代价= 8-max(sum[i],i=1,2,3)
● 剪枝:排除等效冗余。假如先A后F,那么结果其实没有变化,视为无效操作。所以,要避免相邻两次的操作是对立的。
【算法代码】
/*
0 1
2 3
4 5 6 7 8 9 10
11 12
13 14 15 16 17 18 19
20 21
22 23
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=24;
int in[maxn]; //Store 24 cells using 1,2,or 3.
//Eight operations. Each operation has 7 cells.
//The contents of op[8][7] are cells' IDX.
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[]= {6,7,8,11,12,15,16,17}; //cells' IDX of the center 8 cells
int inver[]= {5,4,7,6,1,0,3,2}; //inverse operations' IDX
int ans[100]; //The elements of the output sequence
int h() { //evaluation function
int sum[4]; //sum[i] save the number of the cells which content is i.
memset(sum,0,sizeof(sum));
//The number of cells which contents are 1, 2, or 3
for(int i=0; i<8; i++) sum[in[center[i]]]++;
int s=0; //The max number of central 8 cells with values of 1,2 or 3
for(int i=1; i<=3; i++) s=max(s,sum[i]);
return 8-s;
}
bool check() { //Check the values of the central 8 cells whether are equal
for(int i=1; i<8; i++)
if(in[center[i]]!=in[center[0]]) return false;
return true;
}
void move(int x) { //move cells
int t=in[op[x][0]]; //The IDX of the first cell of each moveation
for(int i=0; i<6; i++) in[op[x][i]]=in[op[x][i+1]]; //move cells
in[op[x][6]]=t;
}
bool dfs(int cur,int dep,int pre) {
if(cur+h()>dep) return false;
if(check()) return true;
for(int i=0; i<8; i++) {
if(inver[i]==pre) continue;
move(i);
ans[cur]=i; //Reasons for next printf("%c",'A'+ans[i]);
if(dfs(cur+1,dep,i)) return true;
move(inver[i]);
}
return false;
}
int main() {
while(scanf("%d", &in[0])) {
if(in[0]==0) break;
for(int i=1; i<maxn; i++) scanf("%d", &in[i]);
int maxd=0;
while(!dfs(0,maxd,-1)) {
maxd++;
}
if(!maxd) printf("No moves needed");
for(int i=0; i<maxd; i++) printf("%c",'A'+ans[i]);
printf("\n%d\n", in[6]);
}
return 0;
}
/*
in:
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
out:
AC
2
DDHH
2
*/
【参考文献】
https://blog.csdn.net/qq_45432665/article/details/104136841
https://www.acwing.com/solution/content/4056/
https://www.cnblogs.com/hhlya/p/13380633.html
https://blog.csdn.net/qq_51392086/article/details/119814637
https://blog.csdn.net/qiaodxs/article/details/125358842
https://www.cnblogs.com/NoahBBQ/p/15149295.html
https://www.cnblogs.com/newblg/p/14679401.html