【BFS + 康托展开 + 打表】hdu 3567 Eight II(八数码问题)

题目描述:

http://acm.hdu.edu.cn/showproblem.php?pid=3567

 

中文大意:

给定八数码的初始状态和目标状态,要求输出移动过程。

最终输出的移动过程需要是所有解决方案中长度最短、字典序最小的。

 

思路:

采用打表的方式来做这道题。

但八数码问题一共有 9! 种状态,以每种状态为广搜起点并记录到达剩余状态的移动信息,工作量太大。

需要将八数码问题抽象成九种初始状态:

012345678

102345678

120345678

123045678

123405678

123450678

123456078

123456708

123456780

例如,初始状态 120453786 和目标状态 123456780,

对应初始状态 120345678 和目标状态 125348670。

状态 120345678 到状态 125348670 的移动过程我们已经知道,按要求输出即可。

其中,移动信息记录在 paths[9][362880] 数组中。

paths[k][i] 中的 k :9 种初始状态,

paths[k][i] 中的 i :现状态的 cantor 值,

pahts[k][i].from :前状态的 cantor 值,

paths[k][i].dir :前状态->现状态的移动方向。

队列节点记录的是当前八数码状态和 “x” 的位置。

在弹出队列首节点,确定了当前八数码的状态信息后,下一步有 4 种选择:“x” 上下左右移动。

“康托展开”用来计算当前状态的 cantor 值,判断该状态是否已经被访问 visited[cantor]。

注意:移动信息不能直接记录在 string 中,否则会超内存限制;

 

代码:

#include<bits/stdc++.h>
using namespace std;

struct node{
    int state[9];
    int pos;
};

int start[9];
int goal[9];

int factory[] = {1,1,2,6,24,120,720,5040,40320,362880};

int Cantor(int state[], int n){
    int result = 0;
    
    for(int i=0;i<n;i++){
        int counted = 0;
        for(int j=i+1;j<n;j++){
            if(state[i] > state[j]){
                counted++;
            }
        } 
        result += counted * factory[n-i-1];
    }
    
    return result;
}

bool visited[362880];

int dir[4][2] = {{0,1}, {-1,0}, {1,0}, {0,-1}};
char dirs[4] = {'d', 'l', 'r', 'u'};

struct path{
    int from;
    char dir;
}paths[9][362880];

void a_start(int k){
    node head,next;
    
    memcpy(head.state, start, sizeof(start));
    head.pos = k;
    
    memset(visited, false, sizeof(visited));
    int h_cantor = Cantor(head.state, 9);
    visited[h_cantor] = true;
    paths[k][h_cantor].from = -1;
       
    queue<node> q;
    q.push(head);
    
    while(!q.empty()){
        head = q.front();
        q.pop();
        
        h_cantor = Cantor(head.state, 9);
        for(int i=0;i<4;i++){
            int x = head.pos % 3;
            int y = head.pos / 3;
            
            int nx = x + dir[i][0];
            int ny = y + dir[i][1];
            
            if(nx>=0 && nx<3 && ny>=0 && ny<3){
                memcpy(next.state, head.state, sizeof(head.state));
                next.pos = ny * 3 + nx;
                swap(next.state[next.pos], next.state[head.pos]);
                
                int n_cantor = Cantor(next.state, 9);
                if(!visited[n_cantor]){
                    visited[n_cantor] = true;
                    
                    paths[k][n_cantor].from = h_cantor;
                    paths[k][n_cantor].dir = dirs[i];
                    
                    q.push(next);
                }
            }
        }
    }
}

void new_start(int k){
    int t = 1;
    for(int i=0;i<9;i++){
        if(i == k){
            start[i] = 0;
        }
        else{
            start[i] = t;
            t++;
        }
    }
}

int main(){
    for(int i=0;i<9;i++){
        new_start(i);
        a_start(i);
    }
    
    int num;
    scanf("%d", &num);
    for(int i=1;i<=num;i++){
        char str[9];
        scanf("%s", str);
        
        int maps[9];
        int k,t = 1; 
        for(int j=0;j<9;j++){
            int num = str[j] - '0';
            if(num > 9){
                k = j;
                num = 0;
                maps[num] = 0;
            }
            else{
                maps[num] = t;
                t++;
            }
        }
        
        scanf("%s", str);
        for(int j=0;j<9;j++){
            int num = str[j] - '0';
            if(num > 9){
                num = 0;
            }
            
            goal[j] = maps[num];
        }
        
        string result;
        int cantor = Cantor(goal, 9);
        while(paths[k][cantor].from != -1){
            result = paths[k][cantor].dir + result;
            cantor = paths[k][cantor].from;
        }
        
        printf("Case %d: %d\n", i, result.length());
        cout<<result<<endl;
    }
}

 

posted @ 2021-02-19 15:12  狂奔的小学生  阅读(140)  评论(0编辑  收藏  举报