【A*算法 + 康托展开 + 曼哈顿距离】hdu 1043 Eight(八数码问题)

题目描述:

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

 

中文大意:

经典八数码问题。

给定初始状态,要求变换到目标状态并输出移动过程。

目标状态固定为:1 2 3 4 5 6 7 8 x 。

 

思路:

采用A*算法 + 康托展开判重 + 曼哈顿距离做启发函数 的方法来做这道题。

初始状态为广搜的起始状态,下一状态的选择依据为估价函数的大小。

估价函数 = 初始状态到当前状态的代价 g + 当前状态到最终状态的代价 h

其中,“当前状态到最终状态的代价”为估计值,有多种计算方式,这里采用“曼哈顿距离”来估计。

两点间的曼哈顿距离:abs(x1 - x2) + abs(y1 - y2)

估价值小的状态,应当优先被处理。

故广搜过程中使用的队列为优先队列,优先级判断的依据为估价函数值(g + h)的大小。

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

②杭电oj 上的这道题有多组数据,需要 while(cin>>temp)。

 

代码:

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

struct node{
    int state[9];//八数码状态 
    int pos;//'x'位置 
    int g,h;//g:bfs层数,即初始状态到现状态的代价
            //h:各点->目标位置的曼哈顿距离之和 
    
    //从小到大排序 
    bool operator<(const node& x)const{
        return (g + h) > (x.g + x.h);
    }
};

int start[9];
int goal[9] = {1,2,3,4,5,6,7,8,0};

//判断开始状态是否有解 
//若逆序数为偶数,则有解
//不统计'x' 
bool have_result(){
    int sum = 0;
    for(int i=0;i<9;i++){
        if(!start[i]){
            continue;
        }
        
        for(int j=i+1;j<9;j++){
            if(start[j] && start[i] > start[j]){
                sum++;
            }
        }
    }
    
    if(sum % 2 == 0){
        return true;
    }
    return false;
}

//移动信息 
struct node2{
    int from;//前状态的 cantor 值 
    int dir;//前状态->现状态的移动方向 
}paths[362880];

//Cantor 计算会用到的常数 
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;
}
 
int gpos[9][2] = {{2,2},{0,0},{1,0},{2,0},{0,1},{1,1},{2,1},{0,2},{1,2}};

//计算曼哈顿距离 
int mht_dis(int state[]){
    int result = 0;
    for(int i=0;i<9;i++){
        if(state[i]){
            int x = i % 3;
            int y = i / 3;
            result += abs(x - gpos[state[i]][0]) + abs(y - gpos[state[i]][1]);
        }
    }
    
    return result;
}

char dir_str[4] = {'u','d','l','r'};

void print(int cantor){
    char result[362880];
    int len = 0;
    //倒序遍历 
    while(paths[cantor].from != -1){
        result[len] = dir_str[paths[cantor].dir];
        len++;
        cantor = paths[cantor].from;
    }
    
    for(int i=len-1;i>=0;i--){
        printf("%c", result[i]);
    }
    printf("\n");
}

bool visited[362880] = {false};
int dir[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};

void a_star(){
    node head,next;
    memcpy(head.state, start, sizeof(start));
    for(int i=0;i<9;i++){
        if(head.state[i] == 0){
            head.pos = i;
            break;
        }
    }
    head.g = 0;
    head.h = mht_dis(head.state);
    
    int h_cantor = Cantor(head.state, 9);
    memset(visited, false, sizeof(visited));
    visited[h_cantor] = true;
    
    paths[h_cantor].from = -1;
        
    priority_queue<node> q;
    q.push(head);
    
    int g_cantor = Cantor(goal, 9);
    while(!q.empty()){
        head = q.top();
        q.pop();
        
        h_cantor = Cantor(head.state, 9);
                
        if(h_cantor == g_cantor){
            print(g_cantor);
            return;
        }
        
        int px = head.pos % 3;
        int py = head.pos / 3;
        
        for(int i=0;i<4;i++){
            int nx = px + dir[i][0];
            int ny = py + 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;
                    
                    next.g = head.g + 1;
                    next.h = mht_dis(next.state);
                    
                    paths[n_cantor].from = h_cantor;
                    paths[n_cantor].dir = i;
                    
                    q.push(next);
                }
            }
        }
    }
}

int main(){
    char temp;
    while(cin>>temp){
        start[0] = temp - '0';
        if(start[0] > 9){
            start[0] = 0;
        }
        
        for(int i=1;i<9;i++){
            cin>>temp;
            
            start[i] = temp - '0';
            if(start[i] > 9){
                start[i] = 0;
            }
        }
        
        if(!have_result()){
            cout << "unsolvable" << endl;
        }
        else{
            a_star();
        }
    }
} 

 

posted @ 2021-02-07 23:25  狂奔的小学生  阅读(973)  评论(0编辑  收藏  举报