USACO 3.2 Magic Squares (msquare)

/*
这道题类似于八数码难题,基本思想是宽搜,使用Hash判重。如果使用一般的八维数组空间可以达到8^8=16777216,
会超过USACO的16MB空间限制。所以我们应该对状态进行散列存储,观察发现每位的数字不能重复,
存在空间冗余。我们可以对于每个状态建立一个映射,采用康托展开算法。(参见Nocow) 定义cantor(s)为s串大大小顺序。
可样将哈希容量缩减到8!=40320。

另外发现,对于魔板当前状态可以由一个8位的8进制数来表示,即无符号32位长整型 unsigned long 表示。 
对于魔板的变换可以转化为对一个数字的位运算。这样可以大大提高程序效率。 
Refer to byvoid. 

本题中我自己使用 int 来表示魔板的当前的状态。因为8位8进制数不会使 int 变为负数,使得移位操作符合本题要求。
*/

/*
ID: haolink1
PROG: msquare
LANG: C++
*/

//#include <iostream>
#include <fstream>
#include <queue> 
#include <string>
using namespace std;
const int MAX = 40320;
bool hash[MAX];
long fac[8]={1,1,2,6,24,120,720,5040};
ifstream fin("msquare.in");
ofstream cout("msquare.out");
int target_state = 0;
int node_state[MAX][3]; // Keep nodes's distance, parent and transform way;

int Cantor(int cur_state){
    int x=0,k=0,p=0;
    bool is_visited[8] = {false};
    for(int i = 8; i >= 2; i--){
        k = cur_state >> 3*(i-1);
        cur_state -= k << 3*(i-1);
        is_visited[k] = true;
        p = k;
        for(int j = 0; j <= k-1; j++){
            if(is_visited[j])
                p--;
        }
        x += fac[i-1]*p;
    }
    return x;
}

void Init(){
    int val = 0;
    for(int i = 0; i < 8; i++){
        fin >> val;
        val--;
        target_state += (val << 3*(7-i));
    }
}

int Transform(int cur_state, int trans){
    int final_state = 0, k = 0;
    switch(trans){
        case 0:
            for(int i = 0; i < 4; i++){
                k = ((7 << 3*i) & cur_state) << 3*(7-i*2);
                final_state += k;
                k = ((7 << 3*(7-i)) & cur_state) >> 3*(7-i*2);
                final_state += k;
            }
            break;
        case 1:
            k = (cur_state & 07000) >>(3*3);
            k += (cur_state & 0777) <<3;
            final_state = (cur_state & 070000) << (3*3);
            final_state += (cur_state & 077700000) >> 3;
            final_state += k;
            break;
        case 2:
            final_state = cur_state & 070077007;
            k += (cur_state & 070) <<(5*3);
            k += (cur_state & 0700) >> 3;
            k += (cur_state & 0700000) >>(3*3);
            k += (cur_state & 07000000) >> 3;
            final_state += k;
            break;
    }
    return final_state;
}

void BFS(){
    int intial_state = 0;
    for(int i = 0; i < 8; i++){
        intial_state += (i << 3*(7-i));
    }
    hash[0] = true;
    node_state[0][0] = 0;
    if(intial_state == target_state)
        return;
    queue<int> state_queue;
    state_queue.push(intial_state);
    bool flag = true;
    while((!state_queue.empty()) && flag == true){
        int cur_state = state_queue.front();
        int cur_cantor = Cantor(cur_state);
        state_queue.pop();
        for(int j = 0; j <3; j++){
            int next_state = Transform(cur_state,j);
            int next_cantor = Cantor(next_state);
            if(!hash[next_cantor]){
                hash[next_cantor] = 1;
                state_queue.push(next_state);
                node_state[next_cantor][0] = node_state[cur_cantor][0]+1;//distance 
                node_state[next_cantor][1] = cur_cantor;//parent
                node_state[next_cantor][2] = j;//tranform way;
                if(next_state == target_state){
                    flag = false;
                    break;
                }
            }
        }
    }
}

void Print(){
    int cur_cantor = Cantor(target_state);
    cout << node_state[cur_cantor][0]<<endl;
    string str;
    while(cur_cantor != 0){
        str += 'A'+node_state[cur_cantor][2];
        cur_cantor = node_state[cur_cantor][1];
    }
//    cout << str << endl;
    int len = str.length();
    if(len == 0){
        cout << endl;
        return;
    }
    int counter = 0; 
    for(int i = len-1; i >= 0; i--){
        counter++;
        cout << str[i];
        if(counter == 60){
            cout << endl;
            counter = 0;
        }
    }
    if(counter != 0)
        cout << endl;
}

int main(){
    
    Init();
    //cout << oct << target_state << endl;
    //cout << oct << Transform(target_state,2) << endl;
    BFS();
    Print();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值