zoukankan      html  css  js  c++  java
  • 【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("
    ");
    }
    
    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();
            }
        }
    } 

     

    作者:老干妈就泡面
    本文版权归作者和博客园共有,欢迎转载,但请给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    Leetcode 121. Best Time to Buy and Sell Stock
    Leetcode 120. Triangle
    Leetcode 26. Remove Duplicates from Sorted Array
    Leetcode 767. Reorganize String
    Leetcode 6. ZigZag Conversion
    KMP HDU 1686 Oulipo
    多重背包 HDU 2844 Coins
    Line belt 三分嵌套
    三分板子 zoj 3203
    二分板子 poj 3122 pie
  • 原文地址:https://www.cnblogs.com/bjxqmy/p/14387149.html
Copyright © 2011-2022 走看看