题目是和1150一样的,但是把深度最大为N的条件去掉了。所以搜索空间大了很多。但是时间限制和内存限制又没增大,所以如果按照1150的方法去写,肯定会超时的。所以,剪枝是必要的。
可以判断,其实如果按照之前的代码,会出现很多重复判断的情况,如AA是和空操作是一样的结果,所以,怎么减少这些重复的判断就是我们剪枝要做的事情。
这里,还使用到了康拓展开。其实不用康拓展开应该也可以,只是更耗内存而已。简单地开个8维的数组(http://www.cnblogs.com/sysuwhj/archive/2010/11/28/1890634.html中有实例,即
bool isvisit[8][8][8][8][8][8][8][8]
这一行代码),则需要耗费8^8的空间(模拟12348765的可放回的全排列),实际像11234567这样的数是不可能出现的,实际上我们只需要8!个数就足够了。康托展开就是要节省那些多出来的空间的。
至于康托展开,百度百科要比维基百科解释得详细,且贴了代码,可以看百度百科介绍:
http://baike.baidu.com/view/437641.htm?fromTaglist
还有一点要注意的是,每次重新跑程序需要将数组的所有值置为false。如
memset(isVisited, false, sizeof(isVisited) );
接下来,直接贴代码:
#include <iostream> #include <queue> #include <string> #include <memory.h> using namespace std; int maxStep; bool isVisited[40320]; int fact[] = { 1, 1, 2, 6, 24, 120, 720, 5040 }; // 从1!开始的阶层 struct Node { string path; int a; }; void toSingleNum(int n, int *single) { // 化为单位数,temp[7]是最低位。 int i; for( i = 7; i >= 0; --i ) { single[i] = n % 10; n /= 10; } } int operate(int mode, Node n) { int a[8]; int j; int tenNum = 10000000; int result = 0; toSingleNum(n.a, a); // 化为单位数 if( mode == 0 ) { // A操作 int temp; for( int i = 0; i < 4; ++i ) { temp = a[i]; a[i] = a[i+4]; a[i+4] = temp; } } else if( mode == 1 ) { // B操作 int temp1, temp2; temp1 = a[3]; temp2 = a[7]; for( int i = 3; i > 0; --i ) { a[i] = a[i-1]; a[i+4] = a[i+3]; } a[0] = temp1; a[4] = temp2; } else { // C操作 int temp; temp = a[1]; a[1] = a[5]; a[5] = a[6]; a[6] = a[2]; a[2] = temp; } for( j = 0; j < 8; ++j ) { result += a[j] * tenNum; tenNum /= 10; } return result; } int cantor(int n) { int i, j, count; int result = 0; int temp[8]; toSingleNum(n, temp); // 将n转化为单位数字存于数组temp for( i = 0; i < 7; ++i ) { // 只需到倒数第二位即可,因为到那最后一位已经确定。 count = 0; for( j = i+1; j < 8; ++j ) { // 计算低位的数字中比该位数小的数的个数 if ( temp[i] > temp[j] ) ++count; } result += count*fact[7-i]; // result = count[7]*7! + count[6]*6! + ... + count[i]*i! + ... + count[1]*1! } return result; // 返回康拓压缩的结果 } void bfs(int goal) { Node u, v; queue<Node> Q; // 定义先进先出队列 u.a = 12348765; u.path = ""; Q.push(u); // 存储起始节点 char c; while( !Q.empty() ) { // 当队列为空时结束循环 u = Q.front(); // 取出队首节点 Q.pop(); // 将队首节点从队列中移除 if( u.path.size() > maxStep ) // 当树节点大于最大步数时输出-1并回到主函数 { cout << "-1" << endl; return ; } if( u.a == goal ) { // 当该节点为目标节点时输出结果并回到主函数 cout << u.path.size() << " " << u.path << endl; return; } int i; for ( i = 0; i < 3; ++i ) { // 对节点进行展开 v.a = operate(i, u); // 分别进行A,B,C操作 c = 'A' + i; // c存储操作的标号 v.path = u.path + c; // 置节点的路径 if(!isVisited[cantor(v.a)]) { // 当该节点没有被访问 isVisited[cantor(v.a)] = true; // 将访问状态标志位真 Q.push(v); // 将节点推进队列。 } } } } int main() { int N; int temp[8]; int theNum; int numTen; int goal; int i; cin >> N; while( N != -1 ) { maxStep = N; memset(isVisited, false, sizeof(isVisited) ); numTen = 10000000; theNum = 0; for( i = 0; i < 8; ++i ) { cin >> temp[i]; theNum += temp[i]*numTen; numTen /= 10; } goal = theNum; bfs(goal); cin >> N; } return 0; }