zoukankan      html  css  js  c++  java
  • Eight HDU

    题目描述

    简单介绍一下八数码问题:
    在一个3×3的九宫格上,填有1~8八个数字,空余一个位置,例如下图:
    1	2	3
    4	5	6
    7	8	 
    在上图中,由于右下角位置是空的,你可以移动数字,比如可以将数字6 
    下移一位:
    1	2	3	 	1	2	3
    4	5	6	→	4	5	 
    7	8	 	 	7	8	6
    或者将数字 8 右移一位:
    1	2	3	 	1	2	3
    4	5	6	→	4	5	6
    7	8	 	 	7	 	8
    1~8按顺序排列的情况称为“初始状态”(如最上方图)“八数码问题”即是
    求解对于任意的布局,将其移动至“初始状态”的方法。
    给定一个的九宫格布局,请输出将它移动至初始状态的移动方法的步骤。
    

    输入

    输入包含多组数据,处理至文件结束。每组数据占一行,包含8个数字
    和表示空位的‘x’,各项以空格分隔,表示给定的九宫格布局。
    例如,对于九宫格
    1	2	3
     	4	6
    7	5	8
    输入应为:1 2 3 x 4 6 7 5 8
    注意,输入的数字之间可能有(不止一个?)空格。
    

    输出

    对于每组输入数据,输出一行,即移动的步骤。向上、下、左、右移动
    分别用字母u、d、l、r表示;如果给定的布局无法移动至“初始 状态”,
    请输出unsolvable。	如果有效的移动步骤有多种,输出任意即可。
    

    首先先介绍一下A*算法

    A*算法是 BFS 的一种改进。

    定义起点 (s) ,终点 (t)

    从起点(初始状态)开始的距离函数 (g(x))

    到终点(最终状态)的距离函数 (h(x), h*(x))

    定义每个点的估价函数 (f(x)=g(x)+h(x))

    A*算法每次从 优先队列 中取出一个 (f) 最小的,然后更新相邻的状态。

    如果 (hleq h*) ,则 A*算法能找到最优解。

    上述条件下,如果 (h) 满足三角形不等式,则 A*算法不会将重复结点加入队列

    其实…… (h=0) 时就是 DFS 算法, (h=0) 并且边权为 (1) 时就是 BFS

    对于八数码问题(A*经典题)

    (h) 函数可以定义为,不在应该在的位置的数字个数。

    容易发现 (h) 满足以上两个性质,此题可以使用 A*算法求解。

    代码实现:

    /*
    A* BFS
    1)首先根据逆序对来判断是否有解
    2)用状态的曼哈顿距离来作为估价函数
    3)BFS {
    	1、预处理,需要存储步数和上一步状态
    	2、定义优先队列来进行搜索
    	3、根据答案倒推出答案
    }
    */
    /*---------------------------------*/
    #include <map>
    #include <cmath>
    #include <queue>
    #include <vector>
    #include <string>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int getval(string str) {        //使用曼哈顿距离作为估价函数
        int cnt = 0;
        for(int i = 0; i < 9; i++) {
            if(str[i] == 'x')   continue;
            int t = str[i] - '1';
            cnt += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);     //计算曼哈顿距离
        }
        return cnt;
    }
    string bfs(string start) {
        string end = "12345678x", opt = "urdl";     //存储目标状态和操作
        int wk[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}};
        map<string, int> dist; 
        map<string, pair<char, string> > prev;    //记录上一步操作和上一步的状态
        priority_queue<pair<int, string>, vector<pair<int, string> >, greater<pair<int, string> > > heap;    //小根堆作为BFS的"队列"
    
        dist[start] = 0;    //初始距离为0
        heap.push(make_pair(getval(start), start));
        while(!heap.empty()) {
            pair<int, string> t = heap.top(); heap.pop();      //取+弹
            //处理当前状态
            string state = t.second;
            if(state == end)    break;
            int x, y;   //存储'x'的坐标
            for(int i = 0; i < 9; i++)
                if(state[i] == 'x') {
                    x = i / 3, y = i % 3;
                    break;
                }
            string str = state;     //存储当前state状态方便复位
            for(int i = 0; i < 4; i++) {
                int dx = x + wk[i][0], dy = y + wk[i][1];
                if(dx >= 0 && dx < 3 && dy >=0 && dy < 3) {
                    state = str;
                    swap(state[x*3 + y], state[dx*3 + dy]);      //四个方向交换
                    if(!dist.count(state) || dist[state] > dist[str] + 1) {   //更新(未到过或不是最短)
                        dist[state] = dist[str] + 1;
                        prev[state] = make_pair(opt[i], str);
                        heap.push(make_pair(dist[state] + getval(state), state));
                    }
                }
            }
        }
        string ans;
        while(end != start) {
            ans += prev[end].first;
            end = prev[end].second;
        }
        reverse(ans.begin(), ans.end());        //翻转得到答案
        return ans;
    }
    int main() {
        char ch;
        while(cin >> ch) {
            string start, judge;        //judge存是否有解,start存起始状态
            start += ch;
            if (ch != 'x') judge += ch;
            for (int i = 0; i < 8; i++) {
                cin >> ch;  start += ch;
                if (ch != 'x') judge += ch;    //judge判断是否有解
            }
            int cnt = 0;        //存储逆序对
            for (int i = 0; i < 8; i++)
                for (int j = i; j < 8; j++)
                    if (judge[i] > judge[j]) cnt++;
            if (cnt & 1) cout << "unsolvable" << endl;   //逆序对为奇数则无解
            else cout << bfs(start) << endl;
        }
        return 0;
    }
    
    //IDA*
    
    

    参考:

    八码数-IDA*算法https://blog.csdn.net/weixin_43769146/article/details/103102622

    八码数有解条件推广https://blog.csdn.net/tiaotiaoyly/article/details/2008233#commentBox

    曼哈頓距離wiki讲解https://zh.wikipedia.org/wiki/%E6%9B%BC%E5%93%88%E9%A0%93%E8%B7%9D%E9%9B%A2

    八数码的八个境界 https://www.cnblogs.com/goodness/archive/2010/05/04/1727141.htmlEight HDU - 1043

  • 相关阅读:
    【模板】2-SAT 问题
    HDU5875 Function
    Codeforces Round #380 (Div. 2)/729B Spotlights 水题
    Codeforces Round #380 (Div. 2)/729E Subordinates 贪心
    Codeforces Round #380 (Div. 2)/729D Sea Battle 思维题
    HDU 5869 Different GCD Subarray Query 树状数组+离线
    HDU 5696 区间的价值 暴力DFS
    HDU 5876 Sparse Graph BFS+set删点
    HDU 5868 Different Circle Permutation Burnside引理+矩阵快速幂+逆元
    HDU 5800 To My Girlfriend DP
  • 原文地址:https://www.cnblogs.com/RioTian/p/13099253.html
Copyright © 2011-2022 走看看