sicily 1151. 魔板[Special judge]

   题目是和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;
}


你可能感兴趣的:(空间,剪枝,拓展,康托展开)