题目描述
在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
下面是对基本状态进行操作的示范:
A:
8 7 6 5
1 2 3 4
B:
4 1 2 3
5 8 7 6
C:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
【输入格式】
输入有多组测试数据
只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间),表示目标状态。
【输出格式】
Line 1: 包括一个整数,表示最短操作序列的长度。
Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。
Sample Input
2 6 8 4 5 7 3 1
Sample Output
7
BCABCCB
题目有点坑。
坑一:是求由基本状态转换为目标状态(给出的状态)的最小步数,而不是由目标状态(给出的状态)转换为基本状态的最小步数。
坑二:题目输出的序列转换但二维数组中并不是按常规方法转换的。
比如基本状态:
1 2 3 4 5 6 7 8
转换到二维数组中为:
1 2 3 4
8 7 6 5
虽然这么说了,但到时候你大概率还是会犯这个错误(无奈ing)。
技巧一:对ABC三种操作,我将操作数放入数组op中(详见代码及其注释),然后bfs时循环进行交换操作。
技巧二:记录操作需要使用vector,并且将vector放入保存状态的结构体中,保证它和状态紧密联系。判重还是使用比较好用的map。
接下来贴出代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
using namespace std;
//关键的技巧,每两个元素分别对应ABC操作所需要交换的位置,-1代表这个操作结束,顺序不能改变,尤其是B操作。其中op数组的一维代表A操作,二三维依次为B、C操作
int op[3][12] = { {0,4,1,5,2,6,3,7,-1,-1,-1,-1},{3,2,7,6,2,1,6,5,1,0,5,4},{2,1,1,5,5,6,-1,-1,-1,-1,-1,-1} };
char opp[3] = { 'A','B','C' };//bfs中的for循环的i映射为ABC字符
int aim = 0;//目标状态
map<int, bool> m;//记录以及存在的状态
struct node {
int num, step;//状态,以及步数
vector<char> v;//记录这一条线的操作,方便找到答案后输出操作
node(int _num, int _step, vector<char> _v) {
num = _num;
step = _step;
v = _v;
}
};
queue<node> q;
void swap(char* c, int a, int b) {//交换字符串c的a、b位置
char t = c[a];
c[a] = c[b];
c[b] = t;
}
int main() {
int a = 0, begin = 12348765, temp[4] = { 0 };//注意我的begin不是12345678,因为这样方便我们放到数组中思考
char aa[10];
vector<char> v;
while(scanf("%d",&a)==1){
aim=0;
aim+=a;
for (int i = 1; i < 8; i++) {//这里两个for循环得到aim,注意我对aim的处理
scanf("%d", &a);
if (i >= 4) {
temp[7 - i] = a;
continue;
}
aim *= 10;
aim += a;
}
for (int i = 0; i < 4; i++) {
aim *= 10;
aim += temp[i];
}
q.push(node(begin, 0, v));//push基础状态
m[begin] = 1;
while (!q.empty()) {//不空则继续
int op1 = 0, op2 = 0, tnum = 0;
char cur[10], cur_t[10];
node temp = q.front();
q.pop();
if (temp.num == aim) {
printf("%d\n", temp.step);
for (int i = 0; i < temp.v.size(); i++) {
printf("%c", temp.v[i]);
}
printf("\n");
}
sprintf(cur, "%d", temp.num);
strcpy(cur_t, cur);//cur_t数组方便之后将cur复原
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 12; j += 2) {//内层for循环执行这一种操作所需要交换的位置
op1 = op[i][j], op2 = op[i][j + 1];
if (op1 == -1) break;//当操作数是-1时,代表已经执行完交换操作
swap(cur, op1, op2);
}
sscanf(cur, "%d", &tnum);
if (m.count(tnum) == 0) {
m[tnum] = 1;
temp.v.push_back(opp[i]);//将这一次操作放入v中
q.push(node(tnum, temp.step + 1, temp.v));//存入
temp.v.pop_back();//将temp中的v还原,以便进行下一个操作
}
strcpy(cur, cur_t);//复原cur
}
}
while(!q.empty()) q.pop();//接下来的就是善后工作,以方便写一个测试数据可以正常处理
m.clear();
}
return 0;
}
都看到这了,不妨点个赞吧!