在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板。魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示。任一时刻魔板的状态可用方块的颜色序列表示:从魔板的左上角开始,按顺时针方向依次写下各方块的颜色代号,所得到的数字序列即可表示此时魔板的状态。例如,序列(1,2,3,4,5,6,7,8)表示魔板状态为:
1
2 3 4
8 7 6 5
对于魔板,可施加三种不同的操作,具体操作方法如下:
A:
上下两行互换,如上图可变换为状态87654321
B: 每行同时循环右移一格,如上图可变换为41236785
C:
中间4个方块顺时针旋转一格,如上图可变换为17245368
给你魔板的初始状态与目标状态,请给出由初态到目态变换数最少的变换步骤,若有多种变换方案则取字典序最小的那种。
hdu 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1430
BFS + 康拓展开 + 打表 + 映射
#include <bits/stdc++.h> const int N = 8; const int MAX = 40323; using namespace std; /* 魔板 hdu 1430 搜索 + 康拓展开。 http://acm.hdu.edu.cn/showproblem.php?pid=1430 BFS + 打表预处理 + 康拓展开 https://www.cnblogs.com/H-Vking/p/4346004.html 关于打表预处理: 由于魔板的所有状态都可以转换为“12345678”, 所以这时就需要做一个映射:每组数据都有一个起始状态与目标状态, 可以把起始状态用一种映射关系映射为“12345678”, 然后用这种映射关系再去改一下终止状态。例如:初态为“12653487” , 目态为“12345678” ;这时映射后初态为“12345678”, 即f[1] = 1 , f[2] = 2 , f[6] = 3 , f[5] = 4 , f[3] = 5 , f[4] = 6 , f[8] = 7 , f[7] = 8 ,按照这种 映射关系目态应为“12564387”。 代码应为:f[start[i] - '0'] = i ; end[i] = f[end[i] - '0'] + '0'; 有这样一个映射前提, 可以先用BFS预处理从“12345678”到其余所有状态的步骤, 然后输入每组测试数据后进行转换,然后这时候就变成了 求从“12345678”到映射后的目标状态的步骤的问题, 这时按照存好的路径输出即可。 */ struct no { int num;//上一状态的编号 int c; no(){ num = -1;c =-1; } }dis[MAX]; typedef struct { int num;//对应kt编号 string st; }node; int fac[] = {1,1,2,6,24,120,720,5040,40320}; string str;//初始状态 string Tstr;//目标状态 int Tflag ; int q; queue<node> qu; //康拓的逆(这题不需要) void ktn(char a[9],int k) { k--;//这别忘了,第12个,一定从11开始计算 int vis[10]={0};int j = 0; for (int i=0;i<N;++i) { int t = k/fac[N-i-1]; for ( j=1;j<=N;++j) //计算出它到底是几,这里要排除出现过的数 if (!vis[j]) { if (!t) break; t--; } a[i]=j+'0'; vis[j] =1; k %= fac[N-i-1]; } } //康拓展开 int kt(string &arr) { int ans = 0 ; for (int i=0;i<N;++i) { int t = 0; //记录后面比它小的数字个数 for (int j = i+1;j<N;++j) { if (arr[j] < arr[i]) t++; } ans += t*fac[N-i-1]; } return ans; } string change (string t,int s) { string a(t); if (s == 1) { for(int i=N-1;i>=0;--i) a[N-i-1] = t[i]; } else if (s == 2) { a[0] = t[3]; a[7] = t[4]; int v = 1; for (int i=0;i<N;++i) { if (i==3 || i == 4) continue; a[v++] = t[i]; } } else { char s1 = t[1];char s2 = t[2]; char s5 = t[5];char s6 = t[6]; a[2] = s1; a[5] = s2;a[6] = s5; a[1]=s6; } return a; } void bfs() { node temp ; string s; int v; while(!qu.empty()) { temp = qu.front();qu.pop(); for (int i=1;i<=3;++i) // A,B,C { s = change(temp.st,i); v =kt(s); if(dis[v].num == -1) { node t ;t.st = s;t.num = v; qu.push(t); dis[v].num = temp.num;dis[v].c =i; } } } } void putans() { char ans[MAX]; int i = 0; int j = Tflag; while(j!= q) { ans[i++] = dis[j].c+'A'-1; j = dis[j].num; } for (int j = i-1;j>=0;--j) printf ("%c",ans[j]); printf (" "); } int main () { str = "12345678"; char s1[10]; q = kt(str); node t ;t.st = str;t.num = q; qu.push(t); bfs(); //打表预处理,从12345678 到其他态的路径算出 while(cin>>str>>Tstr) { //利用一种映射关系 将 起始映射为12345678 对应的终态也就映射成另一个了 //然后就可以查询了(比如 23456781 -- 12345678的映射,就是,2对应1,3对应2...) //映射 for (int i=0;i<N;++i) s1[str[i]-'0'] = i+1; //这里,s1 数组就相当于一个函数了,来映射 for (int i=0;i<N;++i) Tstr[i] = s1[Tstr[i]-'0'] +'0'; Tflag = kt(Tstr); putans(); } return 0; }/* 63728145 86372541 */