题目是和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;
}
转载于:https://blog.51cto.com/xuewei/1351316