zoukankan      html  css  js  c++  java
  • A*算法

    用途

    • 用于最小步数模型,可以在庞大的搜索空间中减少搜索范围;
    • 需要注意的是,如果没有路径可以到达终点,那么A*算法还是会搜索所有的状态.这种情况下,A*算法每选择一个状态的时间复杂度为O(logn),而普通BFS只需要O(1)
    • 其次,A*算法一般应用于非负权边图;如果含负权边且不存在负环,则也可以应用(待定)

    原理:

    • A*算法的核心在于估价函数,用于估算当前状态到终点的步数;
    • 假设实际距离为g(state),估计距离为f(state),则估价函数必须满足:f(state) <= g(state)
    • 越接近则算法效果越好,如果估计值全部为0,则退化为dijkstra算法
    • A*算法使用小根堆,按照d(u) + f(u)排序 (假设到达状态u已用步数为d(u))

    性质

    1.当终点状态第一次出队,即表示找到了最小步数

    证明-反证法

    • 假设不是最小步数,即有:d(end) > d(优)
    • 说明堆中尚存在一条最优路径上的点u满足d(优) = d(u) + g(u) >= d(u) + f(u)(堆中至少包含起点)
    • -> d(end) > d(优) >= d(u) + f(u) , 这与小根堆的性质产生矛盾,说明假设错误

    2.除终点外,每一点可能被更新多次;

    3.A*算法不能保证到每一点(除终点外)在入队/出队时距离是最优的,这一点与dijkstra算法区分

    算法步骤

    while(q.size())
    {
        优先队列中堆顶元素出队;
        
        if(堆顶元素==终点状态) break;
    
        使用堆顶元素更新其他状态,对更新的状态进行估价,并将其入队
    }
    

    例题 -八数码

    题目链接

    题解

    • 估计函数:计算当前状态下每一数字距离其正确位置的曼哈顿距离之和
    • 八数码问题是否有解:计算对应字符串中逆序对的个数,如果为偶数则有解,反之无解。大概的解释是:每一次变化对于逆序对的影响必定是偶数个,且正解中逆序对的个数也是偶数

    代码

    #include <iostream>
    #include <cstring>
    #include <queue>
    #include <unordered_map>
    #include <algorithm>
    using namespace std;
    typedef pair<int , string> PIS;
    string start;
    int dx[] = {-1 , 1 , 0 , 0} , dy[] = {0 , 0 , -1 , 1};
    
    //估价函数
    int f(string str)
    {
        int res = 0;
        for(int i = 0; i < 9; i ++)
        {
            if(str[i] !='x')
            {
                int t = str[i] - '1';
                res += abs(i/3 - t/3) + abs(i%3 - t%3);
            }
        }
        return res;
    }
    
    string bfs()
    {
        string over = "12345678x";
        string op = "udlr";
        
        priority_queue<PIS , vector<PIS> , greater<PIS> > heap;
        unordered_map<string , int> dist;
        unordered_map<string , pair<char,string>> prev;
        heap.push({f(start) , start});
        dist[start] = 0;
        
        while(heap.size())
        {
            PIS t = heap.top();
            heap.pop();
            
            string state = t.second;
            if(state == over) break;
            
            int d = dist[state];
            
            int u = state.find('x');
            int a = u / 3 , b = u % 3;
            for(int i = 0; i < 4; i++)
            {
                int x = a + dx[i] , y = b + dy[i];
                if(x < 0 || x >= 3 || y < 0 || y >= 3) continue;
                
                swap(state[u] , state[x*3 + y]);
                if(!dist.count(state) || dist[state] > d + 1)
                {
                    dist[state] = d + 1;
                    prev[state] = {op[i] , t.second};
                    heap.push({f(state) + dist[state] , state});
                }
                swap(state[u] , state[x*3 + y]);
            }
        }   
        
        string res;
        //对状态转移进行回溯,记录操作(udlr)
        while(over != start)
        {
            res += prev[over].first;
            over = prev[over].second;
        }
        
        reverse(res.begin() , res.end());
        return res;
    }
    
    int main()
    {
        string t;
        for(int i = 0; i < 9; i++)
        {
            char a;  cin >> a;
            start += a;
            if(a != 'x') t += a;
        }
        
        int cnt = 0;
        for(int i = 0; i < 8; i++)
            for(int j  = i + 1; j < 8; j++)
                if(t[i] > t[j]) cnt++;
        
        if(cnt & 1) puts("unsolvable");
        else cout << bfs() << endl;
        
        return 0;
    }
    

    例2-第k短路

    题目链接

    题解

    1.终点第几次出队,即表示第几短路,证明与最短路类似

    2.估价函数:反向建边,跑一遍dijkstra算法,即可以求出与真实值相同的估计值

    代码

    #include <iostream>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define x first
    #define y second
    typedef pair<int,int> PII;
    typedef pair<int,PII> PIII;
    
    const int N = 1010 , M = 200010;
    int e[M] , ne[M] , w[M] , sh[N] , eh[N] , idx;
    int dist[N] , cnt[N] , n , m;
    bool st[N];
    int S , T , K;
    
    void add(int l ,int r , int v , int h[])
    {
        e[idx] = r , w[idx] = v , ne[idx] = h[l] , h[l] = idx ++;
    }
    
    void dijkstra()
    {
        memset(dist , 0x3f , sizeof dist);
        dist[T] = 0;
        priority_queue<PII , vector<PII> , greater<PII> > heap;
        heap.push({0 , T});
        
        while(heap.size())
        {
            PII t = heap.top();
            heap.pop();
            
            int d = t.x , u = t.y;
            if(st[u]) continue;
            
            for(int i = eh[u]; ~i; i = ne[i])
            {
                int v = e[i];
                if(dist[v] > dist[u] + w[i])
                {
                    dist[v] = dist[u] + w[i];
                    heap.push({dist[v] , v});
                }
            }
            st[u] = true;
        }
    }
    
    int astar()
    {
        priority_queue<PIII , vector<PIII> , greater<PIII> > heap;
        heap.push({dist[S] , {0 , S}});
        
        while(heap.size())
        {
            PIII t = heap.top();
            heap.pop();
            
            int d = t.y.x , u = t.y.y;
            cnt[u]++;
            if(cnt[T] == K) return d;
            
            for(int i = sh[u]; ~i; i = ne[i])
            {
                int v = e[i];
                
                int tmp = d + w[i];
                if(cnt[v] < K)  //对于在极端情况下可能会有问题,这里是对正确性和时间做了下权衡。
                    heap.push({tmp + dist[v] , {tmp , v}});
            }
        }
        
        return -1;
    }
    
    int main()
    {
        cin >> n >> m;
        
        memset(sh , -1, sizeof sh);
        memset(eh , -1, sizeof eh);
        for(int i = 0; i < m; i++)
        {
            int l , r , v;
            cin >> l >> r >> v;
            add(l ,r , v , sh) , add(r , l , v , eh);
        }
        
        cin >> S >> T >> K;
        if(S == T) K++;  //注意特殊数据:S==T,则 K++剔除自己走到自己的情况
        
        dijkstra();
        
        printf("%d
    " , astar());
        
        return 0;
    }
    

    参考资料

    Acwing-算法提高课-搜索章节:
    https://www.acwing.com/activity/content/introduction/16/

  • 相关阅读:
    监控kubernetes集群的方式
    Prometheus的集群与高可用
    Grafana简单用法
    Prometheus实战之配置汇总
    Leetcode Surrounded Regions
    leetcode Spiral Matrix II
    leetcode Regular Expression Matching
    leetcode Set Matrix Zeroes
    leetcode 较难题II
    Leetcode 数独
  • 原文地址:https://www.cnblogs.com/zy200128/p/14018758.html
Copyright © 2011-2022 走看看