这三道题目大体相同,只是数据处理方式不同,需要修改的地方很少,因此只以1150为例说明即可。
魔板由8个大小相同方块组成,分别用涂上不同颜色,用1到8的数字表示。
其初始状态是
1 2 3 4
8 7 6 5
对魔板可进行三种基本操作:
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
用上述三种基本操作,可将任一种状态装换成另一种状态。输入包括多个要求解的魔板,每个魔板用三行描述。
第一行步数N(不超过10的整数),表示最多容许的步数。
第二、第三行表示目标状态,按照魔板的形状,颜色用1到8的表示。
当 N 等于 -1 的时候,表示输入结束。对于每一个要求解的魔板,输出一行。
首先是一个整数M,表示你找到解答所需要的步数。接着若干个空格之后,从第一步开始按顺序给出M步操作(每一步是A、B或C),相邻两个操作之间没有任何空格。
注意:如果不能达到,则 M 输出 -1 即可。4 5 8 7 6 4 1 2 3 3 8 7 6 5 1 2 3 4 -1
2 AB 1 A 评分:M超过N或者给出的操作不正确均不能得分。
本题因为题目的特殊性有两种解法,一种是比较普通的广搜法(BFS),另外一种是利用魔板性质构造哈希表的方法。两者相较,方法一更直观,普适性强些,方法二更巧妙,当然速度更快些。同时,本题又要注意数据处理,因为到1151和1515的时候,数据量变大,对数据存储和计算要求变高,所以并不推荐用数组存储魔板状态。观察下发现,魔板只有八个格子,完全可以将八个数字变成一串;而想到整数计算和存储性质仍不够理想,可以用3位二进制数表示单个的数字位,那样变化函数就变成了位运算,速度大幅增加,同时存储要求也降低了很多。(当然,用数组做也可以用一些数据压缩的办法,但是会让数据存储和运算部分变得复杂,并不推荐。)
解法一:
因为有三种变化方法,所以很直观的想到直接搜索。视本题要求为找出图中特定起点和重点间的通路,且通路长度有限制,自然会想到BFS方法。直接套用BFS方法,即可得出答案。
解法二:
本题题目很特殊,魔板的三种变化其实每一种都是可以循环一定次数再回到初始状态的,而且明显状态A到状态B的操作反过来就可以得到状态B到状态A的操作流程。同时,我们发现,解法一每次操作都要从相同的起点开始查找相同的路径,这样太浪费时间,可以制作一个哈希表存储广搜法的遍历结果。然后,将原先的初始状态作为目标状态,根据循环操作的性质不断回溯,找到如何构造它的方法,将这一操作序列反向输出就是答案。
解法一:直接广度优先搜索
// Problem#: 1150 // Submission#: 1786358 // The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License // URI: http://creativecommons.org/licenses/by-nc-sa/3.0/ // All Copyright reserved by Informatic Lab of Sun Yat-sen University #include <iostream> #include <queue> #include <string> #include <cstring> using namespace std; int a[8] = { 1 , 2 , 3 , 4 , 8 , 7 , 6 , 5 } ; bool visit[1<<24]; struct node{ int code; string path; node(){ code = 0; path = ""; } }begin,end; inline int A( int n) { return (n & 4095 ) << 12 | n >> 12 ;} inline int B( int n) { return (( 7 << 9 | 7 << 21 ) & n) >> 9 | ( ~ ( 7 << 9 | 7 << 21 ) & n) << 3 ;} inline int C( int n) { return ( 7 | 7 << 9 | 7 << 12 | 7 << 21 ) & n | (( 7 << 3 ) & n) << 3 | (( 7 << 6 ) & n) << 12 | (( 7 << 18 ) & n) >> 3 | (( 7 << 15 ) & n) >> 12 ;} inline int zip( int a[]) { int s = 0 ; for ( int i = 0 ;i < 8 ; ++ i) s |= (a[i] - 1 ) << ( 3 * i); return s; } void bfs(int n){ queue<node> buffer; buffer.push(begin); visit[begin.code] = true; node t; int v; while(!buffer.empty()){ t = buffer.front(); buffer.pop(); if(t.path.size()>n){ cout << "-1" << endl; return ; } if(t.code==end.code){ cout << t.path.size() << " " << t.path << endl; return ; } node in; v = A(t.code); if(!visit[v]){ visit[v] = true; in.code = v; in.path = t.path + "A"; buffer.push(in); } v = B(t.code); if(!visit[v]){ visit[v] = true; in.code = v; in.path = t.path + "B"; buffer.push(in); } v = C(t.code); if(!visit[v]){ visit[v] = true; in.code = v; in.path = t.path + "C"; buffer.push(in); } } } int main(){ int n; int temp[8],re; begin.code = zip(a); while(cin>>n && n!=-1){ for( int i=0 ; i<8 ; i++ ) cin >> temp[i]; end.code = zip(temp); memset(visit,0,sizeof(visit)); bfs(n); } return 0; }解法二:哈希表
// Problem#: 1150 // Submission#: 1781837 // The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License // URI: http://creativecommons.org/licenses/by-nc-sa/3.0/ // All Copyright reserved by Informatic Lab of Sun Yat-sen University #include <cstdio> char hash[ 1 << 24 ]; inline int A( int n) { return (n & 4095 ) << 12 | n >> 12 ;} // 4095=2^12-1 inline int B( int n) { return (( 7 << 9 | 7 << 21 ) & n) >> 9 | ( ~ ( 7 << 9 | 7 << 21 ) & n) << 3 ;} inline int C( int n) { return ( 7 | 7 << 9 | 7 << 12 | 7 << 21 ) & n | (( 7 << 3 ) & n) << 3 | (( 7 << 6 ) & n) << 12 | (( 7 << 18 ) & n) >> 3 | (( 7 << 15 ) & n) >> 12 ;} inline int zip( int a[]) { int s = 0 ; for ( int i = 0 ;i < 8 ; ++ i) s |= (a[i] - 1 ) << ( 3 * i); return s; } int a[] = { 1 , 2 , 3 , 4 , 8 , 7 , 6 , 5 } ; const int QLen = 10000 ; int q[QLen],b = 0 ,e = 0 ; inline void inc( int & p){ if(++ p ==QLen) p=0;} int main(){ int i,j,n,bgn=zip(a); hash[bgn]='E'; q[b]=bgn; inc(b); while(b!=e){ i=q[e]; inc(e); j=A(i); if(!hash[j]) hash[j]='A', q[b]=j, inc(b); j=B(i); if(!hash[j]) hash[j]='B', q[b]=j, inc(b); j=C(i); if(!hash[j]) hash[j]='C', q[b]=j, inc(b); } char s[100]; while(scanf("%d",&n),n!=-1){ for(i=0; i<8; ++i) scanf("%d",&a[i]); for(i=zip(a),j=0; i!=bgn; ++j){ s[j]=hash[i]; switch(s[j]){ case 'A': i=A(i); break; case 'B': i=B(B(B(i))); break; case 'C': i=C(C(C(i))); break; } } if(j>n) printf("-1\n"); else{ printf("%d ",j); while(j--) putchar(s[j]); putchar('\n'); } } return 0; }