• 启发式搜索: A*算法


    回顾: 优先队列BFS、最短路

    普通BFS:按层扩展
    优先队列BFS:每次从队列中取出当前代价最小的状态进行扩展

    优先队列BFS的局限性:
    一个状态的当前代价最小,只能说明从起始状态到该状态的代价很小,而在未来的搜索中,从该状态到目标状态可能会花费很大的代价。反之亦然。当前代价较大,也许未来代价较小,总代价反而更优。优先队列BFS缺少对未来的预估。

    在这里插入图片描述

    A*算法 – 估价函数

    A*算法是一种启发式搜索(Heuristically Search)算法

    A*算法的关键是设计一个估价函数:

    • 以任意“状态”为输入,计算出从该状态到目标状态所需代价的估计值
    • 在搜索中,维护一个堆(优先队列),优先选择“当前代价+未来估价”最小的状态进行扩展

    估价函数的设计原则:估值必须比实际更优(估计代价≤未来实际代价)
    只要保证上述原则,当目标状态第一次从堆中被取出时,就得到了最优解

    为什么?

    把好状态估差的后果:
    本来在最优解搜索路径上的状态被错误地估计了较大的代价,被压在堆中无法取出,从而导致非最优解搜索路径上的状态不断扩展,直至在目标状态上产生错误的答案
    把坏状态估好的后果:
    只要估价不大于未来实际代价,这个值总会比最优解更早地被取出,从而得到修正。最坏后果无非就是算的状态多了,跑得慢一些。

    否决一个正确idea vs 多看一个垃圾idea

    A*算法

    A*和优先队列BFS的区别就是:考虑优先级的时候有没有加上未来估价

    估价越精准(接近但不超过未来实际代价),A*算法越快
    估价等于0,就退化为了优先队列BFS

    A*算法的关键:开动脑筋,设计优秀的估价函数(必须要乐观估计,但也要尽量精准)

    例如:求第K短路,把当前结点到终点的最短路作为估价函数(最短≤K短)
    优先选择“当前走过的路径长度+估价函数”最小的状态扩展

    实战

    773.滑动谜题
    https://leetcode.cn/problems/sliding-puzzle/

    class Solution {
    public:
        int slidingPuzzle(vector<vector<int>>& board) {
            string start = zip(board);
            string target = zip({{1, 2, 3}, {4, 5, 0}});
            //q.push(start);
            q.push({-evaluate(start), start});
            depth[start] = 0;
    
            while (!q.empty()) {
                string s = q.top().second;
                q.pop();
    
                int pos = findZeroIndex(s);
    
                if (pos >= 3) expand(s, pos, pos - 3);
                if (pos <= 2) expand(s, pos, pos + 3);
                if (pos % 3 != 0) expand(s, pos, pos - 1);
                if (pos % 3 != 2) expand(s, pos, pos + 1);
    
                if (depth.find(target) != depth.end()) return depth[target];
            }
            return -1;
        }
    
    private:
        int evaluate(string &s) {
            int now[6];
            for (int i = 0; i < 6; i++) {
                if (s[i] != '0')
                    now[s[i] - '0'] = i;
            }
    
            int target[6] = {0 ,0 ,1, 2, 3, 4};
            int ans = 0;
            
            for (int digit = 1; digit <= 5; digit++) {
                int nowx = now[digit] / 3;
                int nowy = now[digit] % 3;
                int targetx = target[digit] / 3;
                int targety = target[digit] % 3;
                ans += abs(nowx - targetx) + abs(nowy - targety);
            }
            return ans;
        }
    
        void expand(string& s, int pos, int other) {
            string ns = s;
            swap(ns[pos] , ns[other]);
    
            if (depth.find(ns) != depth.end()) return;
    
            depth[ns] = depth[s] + 1;
            q.push({ - depth[ns] - evaluate(ns), ns});
            //q.push(ns);
        }
    
        string zip(const vector<vector<int>>& board) {
            string res;
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 3; j++) {
                    res += '0' + board[i][j];
                }
            }
            return res;
        }
    
        int findZeroIndex(string& s) {
            for (int i = 0; i < 6; i++) if (s[i] == '0') return i;
            return -1;
        }
    
        //queue q;
        priority_queue<pair<int, string>> q;
        unordered_map<string, int> depth;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    选做题
    八数码
    https://www.acwing.com/problem/content/847/
    https://www.acwing.com/problem/content/181/

    推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

  • 相关阅读:
    【JavaEE】JavaScript
    【浙政钉】第一篇:企业内应用免登
    Redis——主从同步
    【链表及其经典问题】
    【附源码】计算机毕业设计SSM图书销售系统设计
    字符串 (3)--- KMP 算法的扩展
    Html飞机大战(十七): 优化移动端
    rebase 和 merge合并代码
    大模型的数据调度
    Linux下的多线程编程:原理、工具及应用(2)
  • 原文地址:https://blog.csdn.net/qq_46118239/article/details/126461810