A*算法初探

注:本文为半转载文章,原文链接为A_L_A_N的博文 总之比原文充实…

/*
	Name: A*
	Copyright: CQHFBZ
	Author: YJ
	Date: 2020/01/26 18:45
	Description: 咕咕咕,一个让人咕的寻路算法...
*/

1   M e e t   A ∗ 1\space Meet\space A* 1 Meet A

A ∗ A* A是一个让人咕咕咕的优秀的寻路 o r or or搜索算法

T A TA TA的中心思路是贪心, B F S BFS BFS,和预估函数

T A TA TA的时间复杂度是 θ ( \theta( θ(出的题能过 ) ) )并不是我懒


2   D o   A ∗ 2\space Do\space A* 2 Do A

此处有一地图:

绿色代表起点( A A A),红色代表终点( B B B),蓝色代表障碍(即不可行的状态),小方格代表一种状态(起点,终点皆为一种状态)

那我们开始搜索c

定义两个列表 O p e n L i s t , C l o s e L i s t OpenList, CloseList OpenList,CloseList代表待搜索列表和已搜索列表( H a s h Hash Hash映射也行)

1. 将起点 A A A加入 O p e n L i s t OpenList OpenList

2. 寻找起点 A A A周围可以到达的格子,将他们加入 O p e n L i s t OpenList OpenList,并将他们的父节点设为起点 A A A

18QNPU.png

3. 将起点 A A A弹出 O p e n L i s t OpenList OpenList并加入 C l o s e L i s t CloseList CloseList

如图,描有浅绿色的边的格子代表在 O p e n L i s t OpenList OpenList里面的格子

描有浅蓝色的边的格子代表在 C l o s e L i s t CloseList CloseList里面的格子

我们要比出那个格子最靠谱,接下来,就是A*算法最核心的公式 F = G + H F=G+H F=G+H

路人:一个三元一次不等式…

其中, F F F代表的经过方格 X X X的预估距离

G G G代表从起点 A A A到方格 X X X的实际距离

H H H代表从方格 X X X到终点 B B B的预估距离

注意1:计算出的 F F F值得尽量小于而又尽量接近实际距离,这跟一个好的 H H H密切相关

18Qa24.png

如上图,我们为了方便讲解,将横着的一步的距离设为 10 10 10,斜着的一步的距离设为 14 14 14,且斜走时旁边不能有障碍我不会告诉你我是为了抠图…

每个已遍历方格的左下角是 G G G,右下角是 H H H,左上角是 F F F

4. 可见 F F F值最小的是起点 A A A的左边方格 C C C,将 T A TA TA周围可到达的格子(除障碍物与在 C l o s e L s i t CloseLsit CloseLsit内的格子)加入 O p e n L i s t OpenList OpenList

停!

我们这时候就要来分类讨论了:

4.1. 某方格 D D D已经在 O p e n L i s t OpenList OpenList里面了

这时候我们就要与前面的 G G G值作比较,取最小的 F F F值,更新路径(即父节点)

4.2. 某方格 D D D还不在 O p e n L i s t OpenList OpenList里面了

那还不加入…

如上图,那 C C C方格的上面 D D D方格来讲,他的新 G G G值是 10 + 10 = 20 10+10=20 10+10=20,而原 G G G值是 14 14 14,所以要保留原来路径,不要绕路

5. 现在我们开始寻找 O p e n L i s t OpenList OpenList中寻找"最靠谱"的方格,但我们发现 C C C上面和下面的F值都是 54 54 54,我们这时候就只能靠运气,随便选一个,就拿下面方格举例,称他为 D D D,将 T A TA TA的周围顶点如 4.1 , 4.2 4.1, 4.2 4.1,4.2一样做处理

注意2:方格 D D D并不是由方格 C C C走来的, T A TA TA是由 T A TA TA的父节点 A A A走来的,这是一个很常见的错误?(顺便说一下,我上一句谈到 C C C只因为让读者知道位置,而非表达一个递进关系…)

完成后如图:

18QdxJ.png
看看与你是否想的一样

( N N N个…)

最后,我们会得到两种结果:

N N N.1. 终点 B B B已加入 O p e n L i s t OpenList OpenList,查找成功

那么就沿着父节点向前搜索,直到起点,再反向输出路径:
在这里插入图片描述

⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ ⇓ \Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow

N N N.2. O p e n L i s t OpenList OpenList已经为空,查找失败

return -1;

3   s u m m a r y   A ∗ 3\space summary\space A* 3 summary A

上伪代码(本人第一次写伪代码,写的不好可以揍我可以喷我,但不可以揍我,因为我是练习时长两年半的个人 O i e r Oier Oier Y J YJ YJ…):

//A_star伪代码//
//前置芝士: 指针//
/*****定义*****/
struct node {
    pair<int, int> pos;
    int G, H, F;
    int father;//0~7, 从上面开始顺时针标号
} Start, End;
int way[2][8] = {{1, 0}, {1, -1}, {0, -1}, {-1, -1}, ......};
vector<node*> OpenList;
vector<node*> CloseList;
/*****定义*****/
/*****过程*****/
node* InList(list<node*> &L, node* find);//寻找find是否在L里面(有返回指针,没返回NULL)
node* MinF();//找出带有最小F值的node指针
bool CanGo(pair<int, int> posx, int wayi);//判断可不可以到达
int GetG(node* X, int wayi);//求G
int GetH(node* X, node* E);//一个优秀的函数
bool AStar(node* S, node* E) {
    S -> G = 0;
    S -> H = S -> F = Get_H(S, E);
    S -> father = -1;
    S加入OpenList;
    while (OpenList不为空) {
        node* X = MinF();
        删除OpenList里面的X;
        X加入CloseList;
        for (i : 0 ~ 7) {
            node* Y = 新的node;
            if (一大堆要求 and CanGo(X -> pos, i) and InList(CloseList, Y) == NULL) {
                if (Y是E) return 1;
                求Y的GHF;
                node* InListY = InList(OpenList, Y);
                if (InList == NULL) {
                    Y -> father = i;
                    Y加入OpenList;
                }
                else {
                    if (Y -> G < InListY -> G) {
                        更改InListY的G, F, father;
                    }
                }
            }
        }
    }
    return 0;
}
vector<node*> GetPath(node* S, node* E) {
    vector<node*> path;
    //读者们请自行思考
    return path;
}
/*****过程*****/
/*****main*****/
int main() {
    //读者们请自行思考
    return 0;
}

489.第k短路

内存限制:128 MiB 时间限制:1000 ms
题目描述

给定一张N个点(编号1,2…N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边。

注意:每条最短路中至少要包含一条边。

输入格式

第一行包含两个整数N和M。

接下来M行,每行包含三个整数A,B和L,表示点A与点B之间存在有向边,且边长为L。

最后一行包含三个整数S,T和K,分别表示起点S,终点T和第K短路。

输出格式

输出占一行,包含一个整数,表示第K短路的长度,如果第K短路不存在,则输出“-1”。

样例
样例输入
2 2
1 2 5
2 1 4
1 2 2
样例输出
14
数据范围与提示

1 <= n <= 1000, 0 <= m <= 100000

1 <= a, b <= N, 1 <= T(权值)<= 100

1 <= s, t <= N, 1 <= k <= 1000

由于路径可以重复,所以 O p e n L i s t , C l o s e L i s t OpenList,CloseList OpenList,CloseList统统不要, H H H函数就是由终点道各点的路径( S P F A SPFA SPFA即可)

c o d e : code: code:

#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e5 + 100;
const int MAXM = 3e3 + 10;

template <typename T>
inline void read(T &x) {
    x = 0;
    T ff = 1, ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            ff = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= ff;
}

template <typename T>
inline void write(T x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

int n, m, s, t, k;
int f[MAXN], vis[MAXN];
int lin[MAXN], tot = 0, linc[MAXN], totc = 0;
struct edge {
    int y, v, next;
} a[MAXN], e[MAXN];

struct node {
    int pos, H, G;
    bool operator < (node a) const { return a.H + a.G < H + G; }
};

inline void add(int xx, int yy, int vv) {
    a[++tot].y = yy;
    a[tot].v = vv;
    a[tot].next = lin[xx];
    lin[xx] = tot;
}

inline void addc(int xx, int yy, int vv) {
    e[++totc].y = yy;
    e[totc].v = vv;
    e[totc].next = linc[xx];
    linc[xx] = totc;
}

void SPFA() {
    queue<int> q;
    memset(f, 0x3f, sizeof(f));
    memset(vis, false, sizeof(vis));
    q.push(t);
    f[t] = 0;
    vis[t] = true;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        vis[x] = false;
        for (int i = lin[x], y; i; i = a[i].next) {
            if (f[y = a[i].y] > f[x] + a[i].v) {
                f[y] = f[x] + a[i].v;
                if (!vis[y]) {
                    vis[y] = true;
                    q.push(y);
                }
            }
        }
    }
}

int astar() {
    priority_queue<node> q;
    if (f[s] == INF)
        return -1;
    int ts[MAXN];
    node tmp, h;
    h.pos = s;
    h.H = 0;
    h.G = 0;
    q.push(h);
    while (!q.empty()) {
        node x = q.top();
        q.pop();
        ts[x.pos]++;
        if (ts[x.pos] == k && x.pos == t)
            return x.G;
        if (ts[x.pos] > k)
            continue;
        for (int i = linc[x.pos]; i; i = e[i].next) {
            tmp.pos = e[i].y;
            tmp.H = f[e[i].y];
            tmp.G = x.G + e[i].v;
            q.push(tmp);
        }
    }
    return -1;
}

int main() {
    read(n);
    read(m);
    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        read(u);
        read(v);
        read(w);
        add(v, u, w);
        addc(u, v, w);
    }
    read(s);
    read(t);
    read(k);
    if (s == t)
        ++k;
    SPFA();
    write(astar());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值