zoukankan      html  css  js  c++  java
  • 八数码问题(三种解决办法)

    题目链接: https://www.luogu.org/problemnew/show/P1379

    题目链接:https://vijos.org/p/1360 (题目一样,上面一个测试数据更多)


    0.问题引入

     在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765)。

    找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

    1. 广度优先遍历

    • 用字符串表示状态。
    • 使用 map<string,bool> 进行判定当前状态是否进行过搜索。
    • 记录进行到第几层BFS,需要一个 9! 的数组(0~8全排列的个数)。

    这题是说明了不会有无解的情况,真正情况下是可能会有无解的情况的,出现在 队列为空还未走到目标状态。

    #include <iostream>
    #include <string>
    #include <queue>
    #include <map>
    #include <algorithm>
    using namespace std;
    
    map<string, bool>m;
    int cnt[400000]; int head = 0, tail = 1;
    string aim = "123804765";
    
    //1:up,2:down,3:left,4:right
    int moveto(int i, int index) {
        switch (i) {
        case 1:
            if (index - 3 >= 0)return index - 3;
            break;
        case 2:
            if (index + 3 <= 8)return index + 3;
            break;
        case 3:
            if (index % 3 != 0)return index - 1;
            break;
        case 4:
            if (index % 3 != 2)return index + 1;
            break;
        }
        return -1;
    }
    
    void BFS(string start) {
        if (start.compare(aim) == 0) {
            cout << '0' << endl;
            return;
        }
        bool flag = false;
        queue<string>q;
        q.push(start);
        
        while (!q.empty() && !flag) {
            string now = q.front(); q.pop();
            int from = now.find('0');
            //向四个方向
            for (int i = 1; i <= 4; i++) {
                int to = moveto(i, from);
                //如果返回-1,当前方向无法到达
                if (to == -1)continue;
                swap(now[from], now[to]);
                if (!m[now]) {
                    if (now.compare(aim) == 0) {
                        cout << cnt[head] + 1 << endl;//输出当前BFS的层数
                        flag = true;
                        break;
                    }
                    cnt[tail++] = cnt[head] + 1;
                    //cnt[tail++] = cnt[head] + 1;
                    m[now] = true;
                    q.push(now);
                }
                swap(now[from], now[to]);
            }
            head++;
        }
    }
    
    int main() {
        string start;
        cin >> start;
        BFS(start);
    
        return 0;
    }
    View Code

       

    2.DBFS 双向广度优先遍历

    双向广度优先,两个队列,一个从起点开始扩展状态,另一个从终点开始扩展状态;如果两者相遇,则表示找到了一条通路,而且是最短的通路。

    双向,必须要记录下来每个状态处于第几层,因为相遇的时候,必须知道这个相遇状态是处于BFS的哪一层。

    #include <iostream>
    #include <string>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    //记录该string状态处于正(反)向的第几层BFS
    map<string, int>fsign, rsign;
    int fcnt[300000];
    int rcnt[300000];
    
    string aim = "123804765";
    
    //1:up,2:down,3:left,4:right
    int moveto(int i, int index) {
        switch (i) {
        case 1:
            if (index - 3 >= 0)return index - 3;
            break;
        case 2:
            if (index + 3 <= 8)return index + 3;
            break;
        case 3:
            if (index % 3 != 0)return index - 1;
            break;
        case 4:
            if (index % 3 != 2)return index + 1;
            break;
        }
        return -1;
    }
    
    bool extend(queue<string>&q, int head, int &tail, int direction) {
        string now = q.front(); q.pop();
        int from = now.find('0');
        for (int i = 1; i <= 4; i++) {
            int to = moveto(i, from);
            if (to != -1) {
                swap(now[from], now[to]);
                if (direction == 1) {
                    if (!fsign[now]) {
                        q.push(now);
                        fsign[now] = fcnt[tail++] = fcnt[head] + 1;
                        if (rsign[now]) {
                            cout << fcnt[tail - 1] + rsign[now] << endl;
                            return true;
                        }
                    }
                }
                else {
                    if (!rsign[now]) {
                        q.push(now);
                        rsign[now] = rcnt[tail++] = rcnt[head] + 1;
                        if (fsign[now]) {
                            cout << rcnt[tail - 1] + fsign[now] << endl;
                            return true;
                        }
    
                    }
                }
                swap(now[from], now[to]);
            }
        }
        return false;
    }
    
    void DBFS(string start) {
        if (start.compare(aim) == 0) {
            cout << '0' << endl;
            return;
        }
        bool flag = false;
        queue<string>forward, reverse;
        forward.push(start);
        reverse.push(aim);
        string forwardNow = start, reverseNow = aim;
        fsign[start] = 0; rsign[aim] = 0;
        int head = 0, tail = 1, rhead = 0, rtail = 1;
        while (!flag) {
            if (forward.size() <= reverse.size()) {
                if (extend(forward, head, tail, 1))return;
                head++;
            }
            else {
                if (extend(reverse, rhead, rtail, 2))return;
                rhead++;
            }
        }
    }
    
    int main() {
        string start;
        cin >> start;
    
        DBFS(start);
    
        return 0;
    }
    View Code

     3.A*(启发式搜索)

    启发式搜索_百度百科

    启发策略:f(n) = g(n) + h(n),其中 g(n) 为层数,h(n)为当前状态“不在位”的数量。建立优先级队列,每次选取 f(n) 最小的状态。

    #include <iostream>
    #include <string>
    #include <map>
    #include <queue>
    using namespace std;
    
    string aim = "123804765";
    map<string, bool>flag;
    
    int getDifferent(string s) {
        int cnt = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] != aim[i])cnt++;
        }
        return cnt;
    }
    class PAIR{
    public:
        int floor;//层数
        int cnt;//"不在位"的块数
        string ss;
        PAIR(int floor, string s) {
            this->floor = floor;
            cnt = getDifferent(s);
            ss = s;
        }
        bool operator<(PAIR f)const {
            return (f.floor + f.cnt) < (floor + cnt);
        }
    };
    priority_queue<PAIR>q;
    
    int moveto(int i, int index) {
        switch (i) {
        case 1:
            if (index - 3 >= 0)return index - 3;
            break;
        case 2:
            if (index + 3 <= 8)return index + 3;
            break;
        case 3:
            if (index % 3 != 0)return index - 1;
            break;
        case 4:
            if (index % 3 != 2)return index + 1;
            break;
        }
        return -1;
    }
    
    void BFS(string start) {
        if (start.compare(aim) == 0) {
            cout << "0" << endl;
            return;
        }
        q.push(PAIR(0, start));
        int head = 0, tail = 1;
        bool sign = false;
        while (!q.empty()&&!sign) {
            PAIR p = q.top(); q.pop();
            string now = p.ss;
            int from = now.find('0');
            for (int i = 1; i <= 4; i++) {
                int to = moveto(i, from);
                if (to != -1) {
                    swap(now[from], now[to]);
                    if (!flag[now]) {
                        if (now.compare(aim) == 0) {
                            cout << p.floor + 1 << endl;
                            sign = true;
                            break;
                        }
                        flag[now] = true;
                        q.push(PAIR(p.floor + 1, now));
                    }
                    swap(now[from], now[to]);
                }
            }
            head++;
        }
    }
    
    int main() {            
        string start;
        cin >> start;
        BFS(start);
        return 0;
    }
    View Code

    但是在这个题目,两个网站上跑得都更慢了,应该数据比较特殊。

     附测试数据:

    /*
    273645801
    out:15
    
    053276184
    out:21
    
    836752104
    out:14
    */
  • 相关阅读:
    PAT Advanced 1073 Scientific Notation (20 分)
    PAT Advanced 1071 Speech Patterns (25 分)
    C++ 正则查找
    PAT Advanced 1022 Digital Library (30 分)
    Linux文件系统及常用命令
    Elasticsearch常用查询
    Sublime Text3 快捷键
    Lua函数
    线程池
    Jdk8新特性之Base64
  • 原文地址:https://www.cnblogs.com/czc1999/p/10428066.html
Copyright © 2011-2022 走看看