HNU_AI_实验1--A*搜索算法求解罗马尼亚问题

一、实验目的

  1. 掌握有信息搜索策略的算法思想;

  2. 能够编程实现搜索算法;

  3. 应用A*搜索算法求解罗马尼亚问题。

二、相关知识-A*搜索

1.算法介绍

  A*算法常用于 二维地图路径规划,算法所采用的启发式搜索可以利用实际问题所具备的启发式信息来指导搜索,从而减少搜索范围,控制搜索规模,降低实际问题的复杂度。

2.算法原理:

  A*算法的原理是设计一个代价估计函数:其中

f(n)=g(n)+h(n)

  • 评估函数F(n)

    • 定义:从起始节点通过节点n的到达目标节点的最小代价路径的估计值;

    • 作用:根据 F(n)可以计算出当前节点的代价,并可以对下一次能够到达的节点进行评估。

  • 函数G(n)

    • 定义:从起始节点到n节点的已走过路径的实际代价;

  • 函数H(n)

    • 定义:从n节点到目标节点可能的最优路径的估计代价 。

    • 作用H(n)表明了算法使用的启发信息,它来源于人们对路径规划问题的认识,依赖某种经验估计。

采用每次搜索都找到代价值最小的点再继续往外搜索的过程,一步一步找到最优路径。

3.评价

  • 完备性:需代价小于等于C*(最优解路径的代价值)的结点是有穷的,每步代价都超过ε并且b是有穷的。

  • 最优性:需满足可采纳性(--树搜索最优)和一致性(图搜索最优)

4.可采纳性与一致性

可接受: 对于任何状态n,满足h(n) h*(n), 其中h*(n)表示从n到目标状态的实际最小代价。

注意:下面的应当是小于等于,而非小于;

一致: 对于任何状态n和其后继状态n',满足h(n) ≤ c(n, n') + h(n'),其中c(n, n')表示从n到n'的实际代价。

5.图搜索与树搜索

参考文章:A*搜索算法的树搜索与图搜索的区别

  • 图搜索:不允许重复访问结点,即OPEN表 ∩ CLOSED表 = ø;
  • 树搜索:允许重复访问结点。

举例:(这也是2018年第二学期的期中考试题)

(1)题目

(2)树搜索

(3)图搜索

感觉上面的图也有些瑕疵,下面是笔者画的:

三、实验内容及步骤

0.实训内容:2-1第三章 通过搜索进行问题求解

题目:罗马尼亚问题

  agent在罗马尼亚度假,目前位于 Arad 城市。agent明天有航班从Bucharest 起飞,不能改签退票。

        现在你需要寻找到 Bucharest 的最短路径,在右侧编辑器补充void A_star(int goal,node &src,Graph &graph)函数,使用编写的搜索算法代码求解罗马尼亚问题:

预期输出:

solution: 0-> 15-> 14-> 13-> 1-> end
cost:418

1.创建搜索树;

(1)给每个城市编号

#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
#define G 6
#define H 7
#define I 8
#define L 9
#define M 10
#define N 11
#define O 12
#define P 13
#define R 14
#define S 15
#define T 16
#define U 17
#define V 18
#define Z 19

(2)启发函数--h数组赋值

结点n距离终点的预计代价,采用直线距离;

int h[20] =//从n节点到目标节点可能的最优路径的估计代价
{ 366,0,160,242,161,
178,77,151,226,244,
241,234,380,98,193,
253,329,80,199,374 };

(3)struct城市节点

  • 每个结点保存其g(n),h(n),f(n)=g(n)+h(n)。

  • 重载<:每次要取出f(n)最小的结点,便于对结点进行排序。

struct node
{
    int g;                              //从起始节点到n节点的已走过路径的实际代价
    int h;                              //从n节点到目标节点可能的最优路径的估计代价
    int f;                              //代价估计函数
    int name;
    node(int name, int g, int h) {       //构造函数
        this->name = name;
        this->g = g;
        this->h = h;
        this->f = g + h;
    };
​
    //重载运算符
    bool operator <(const node& a)const { return f < a.f; }
};

(4)class图

class Graph        //图结构
{
public:
    Graph() {
        memset(graph, -1, sizeof(graph));   //图初始化为-1,代表无边
    }
​
    int getEdge(int from, int to) {       //获取边的开销
        return graph[from][to];
    }
​
    void addEdge(int from, int to, int cost) {    //新增一条边及其开销
        if (from >= 20 || from < 0 || to >= 20 || to < 0)
            return;
        graph[from][to] = cost;
    }
​
    void init() {                        //图初始化
        addEdge(O, Z, 71);
        addEdge(Z, O, 71);
​
        addEdge(O, S, 151);
        addEdge(S, O, 151);
​
        addEdge(Z, A, 75);
        addEdge(A, Z, 75);
​
        addEdge(A, S, 140);
        addEdge(S, A, 140);
​
        addEdge(A, T, 118);
        addEdge(T, A, 118);
​
        addEdge(T, L, 111);
        addEdge(L, T, 111);
​
        addEdge(L, M, 70);
        addEdge(M, L, 70);
​
        addEdge(M, D, 75);
        addEdge(D, M, 75);
​
        addEdge(D, C, 120);
        addEdge(C, D, 120);
​
        addEdge(C, R, 146);
        addEdge(R, C, 146);
​
        addEdge(S, R, 80);
        addEdge(R, S, 80);
​
        addEdge(S, F, 99);
        addEdge(F, S, 99);
​
        addEdge(F, B, 211);
        addEdge(B, F, 211);
​
        addEdge(P, C, 138);
        addEdge(C, P, 138);
​
        addEdge(R, P, 97);
        addEdge(P, R, 97);
​
        addEdge(P, B, 101);
        addEdge(B, P, 101);
​
        addEdge(B, G, 90);
        addEdge(G, B, 90);
​
        addEdge(B, U, 85);
        addEdge(U, B, 85);
​
        addEdge(U, H, 98);
        addEdge(H, U, 98);
​
        addEdge(H, E, 86);
        addEdge(E, H, 86);
​
        addEdge(U, V, 142);
        addEdge(V, U, 142);
​
        addEdge(I, V, 92);
        addEdge(V, I, 92);
​
        addEdge(I, N, 87);
        addEdge(N, I, 87);
    }
​
private:
    int graph[20][20];              //图数组,用来保存图信息,最多有20个节点
};

(5)其他队列、记录路径数组

bool list[20];          //记录节点是否在当前队列中
vector<node> openList;  //扩展队列
bool closeList[20];     //记录节点是否被扩展(选择)过
stack<int> road;        //记录路径
int parent[20];         //记录路径

(6)打印函数

打印路径和开销:void print_result(Graph &graph)

void print_result(Graph &graph)
{
    int p = openList[0].name;
    int lastNodeNum;
    road.push(p);
    while (parent[p] != -1)
    {
        road.push(parent[p]);
        p = parent[p];
    }
    lastNodeNum = road.top();
    int cost = 0;
    cout << "solution: ";
    while (!road.empty())
    {
        cout << road.top() << "-> ";
        if (road.top() != lastNodeNum)
        {
            cost += graph.getEdge(lastNodeNum, road.top());
            lastNodeNum = road.top();
        }
        road.pop();
    }
    cout << "end" << endl;
    cout << "cost:" << cost;
}

2.实现A*搜索算法;

实验平台任务:补充void A_star(int goal,node &src,Graph &graph)函数;

  • 起点入队;

  • 只要扩展队列非空,就不断从扩展队列中取队首节点进行扩展:

    • 扩展结点为目标结点:搜索结束。

    • 扩展结点非目标结点:考察与扩展节点有边相连,且未扩展过的节点:

      • 新结点在扩展队列中:尝试以当前扩展节点为中继,更新g(n)和f(n);

      • 新结点不在扩展队列中:直接构造新结点并加入队列;

  • 扩展结束后,对队列进行排序,保证继续取出f(n)最小的结点扩展。

void A_star(int goal,node &src,Graph &graph)
{
    openList.push_back(src); //起点入队
    sort(openList.begin(), openList.end());
    
    while (!openList.empty())//队列中还有可扩展节点,就不断扩展
    {
        /********** Begin **********/
        node n=openList[0];
        if(n.name==goal) return; //扩展节点是目标节点,搜索结束
        openList.erase(openList.begin());
        closeList[n.name]=1; //当前节点已扩展
        list[n.name]=0; //当前节点出队
        for(int i=0;i<20;i++){
            if(graph.getEdge(n.name,i)!=-1 && closeList[i]!=1){
                int cost=n.g+graph.getEdge(n.name,i);//到下一个城市的代价
                if(list[i]){
                    //更新已有节点
                    for(int j=0;j<openList.size();j++){
                        if(openList[j].name==i){
                            if(openList[j].g>cost){
                                openList[j].g=cost;
                                openList[j].f=cost+openList[j].h;
                                parent[i]=n.name;
                            }
                            break;
                        }
                    }
                }
                else{
                    //构造新节点
                    node newNode(i,cost,h[i]);
                    openList.push_back(newNode);
                    list[i]=1;
                    parent[i]=n.name;
                }
            }
        }
        sort(openList.begin(),openList.end());  
        /********** End **********/  
    }
}
测试版本
void A_star(int goal, node& src, Graph& graph)//A*搜索算法
{
    openList.push_back(src);                    //扩展集合加入起始节点
    sort(openList.begin(), openList.end());     //排序扩展集合的节点,以取出代价最小的节点
    for(auto x:openList) cout<<x.name<<" ";     cout<<endl; 
​
    while (!openList.empty())
    {
        /********** Begin **********/
        node curNode = openList[0];             //取出扩展集合第一个节点,即代价最小的节点        
        cout<<"当前扩展节点:"<<ch[curNode.name]<<endl;
        if (curNode.name == goal) {             //如果当前节点就是目标节点,则退出
            cout<<"\t当前扩展节点为GOAL"<<endl;
            return;
        }
        openList.erase(openList.begin());       //将当前节点从扩展列表中删除
        closeList[curNode.name] = true;         //将当前节点加入已访问节点
        list[curNode.name] = false;             //标记当前节点已不在扩展集合中
​
        for (int i = 0; i < 20; i++) {                      //开始扩展当前节点,即找到其邻居节点
            if (graph.getEdge(i, curNode.name) == -1) {     //若不是当前节点的邻居节点,跳到下一个节点
                continue;
            }
            if (closeList[i]) {                             //若此节点已加入已访问集合closeList,也跳到下一个节点
                cout<<"\t节点--"<<ch[i]<<" 已访问过"<<endl; 
                continue;
            }
            cout<<"\t节点--"<<ch[i]<<" 未访问过,且与当前扩展节点--"<<ch[curNode.name]<<"有边相连"<<endl;
            int g1 = curNode.g + graph.getEdge(i, curNode.name);    //计算起始节点到当前节点i的g值
            int h1 = h[i];                                          //获得当前节点i的h值
            if (list[i]) {                                          //如果节点i在openList中
                cout<<"\t\t节点--"<<ch[i]<<" 在扩展节点集合中"<<endl; 
                for (int j = 0; j < openList.size(); j++) {
                    if (i == openList[j].name) {                    //首先找到节点i的位置,即j
                        if (g1 < openList[j].g) {                   //如果新的路径的花销更小,则更新
                            cout<<"\t\t\t新路径花销更小,更新"<<endl; 
                            openList[j].g = g1;
                            openList[j].f = g1 + openList[j].h;
                            parent[i] = curNode.name;               //记录父节点
                            break;
                        }
                        cout<<"\t\t\t新路径花销不会更优,不更新"<<endl; 
                    }
                }
            }
            else {                        //如果节点i不在openList,则将其加入其中(因为扩展时访问了它)
                cout<<"\t\t节点--"<<ch[i]<<" 不在扩展节点集合中"<<endl;
                cout<<"\t\t\t创建新节点,加入扩展队列"<<endl; 
                node newNode(i, g1, h1);                    //创建新节点,其参数已知
                openList.push_back(newNode);                //新节点加入openList中
                parent[i] = curNode.name;                   //记录父节点
                list[i] = true;                             //记录节点i加入了openList
            }
        }
        cout<<"openList: "; for(auto x:openList) cout<<ch[x.name]<<"-"<<x.f<<" ";   cout<<endl;
        sort(openList.begin(), openList.end());     //扩展完当前节点后要对openList重新排序
        cout<<"openList-sorted: "; for(auto x:openList) cout<<ch[x.name]<<"-"<<x.f<<" ";    cout<<endl<<endl;
        /********** End **********/
    }
}

 输出结果:

 解空间树:

3.使用编写的搜索算法代码求解罗马尼亚问题

4.分析算法的时间复杂度

  • 启发式函数的误差:

    • 绝对误差:\Delta = h^{*}-h

    • 相对误差:\varepsilon = \frac{h^{*}-h}{h^{*}}

  • 时间复杂度

    • 最大绝对误差下的复杂度:O(b^{\Delta})

    • 考虑每步骤代价为常量:O(b^{\varepsilon d})

四、思考题

1.几种搜索算法最优性的比较

宽度优先搜索,深度优先搜索,一致代价搜索,迭代加深的深度优先搜索算法哪种方法最优?

ε每步行动代价都相等时,宽度优先搜索迭代加深的深度优先搜索最优,否则一致搜索代价算法最优

  • 宽度优先算法

    • 完备性:在最浅的目标处于有限深度时是完备的;

    • 最优性:路径代价基于结点深度的非递减函数时才是最优的,最典型的就是行动代价相等的情况;

    • 迭代加深的深度优先搜索类似,且二者时间复杂度与空间复杂度也相同。

  • 一致代价搜索:最优的;

    • 扩展路径消耗最小的结点,由于代价非负,第一个被扩展的目标结点一定是最优解。

    • 但可能会探索代价小的行动的搜索树,开销更大。

  • 深度优先搜索:既不是完备的,也不是最优的。

2.贪婪最佳优先搜索和A*搜索那种方法最优?

A*搜索算法是最优的。

  • 贪婪最佳优先搜索:不具有完备性,也不具有最优性,是否找到最优解与启发式函数有关。

  • A*搜索算法

    • 最优性:满足可采纳性和一致性就是最优的;

    • 完备性:只要分支是有限的就是完备的。

3.分析比较无信息搜索策略和有信息搜索策略

  • 无信息搜索策略

    • 缺点:盲目的搜索,可能需要较大的时间开销和空间开销才能找到解;

    • 优点:具有好的通用性。

  • 有信息搜索策略

    • 缺点:性能与启发式函数的质量有关

    • 优点:通过启发式函数利用问题的额外信息,在搜索过程中向着可能有最优解的方向推进,能够提高搜索效率。

五、代码汇总

1.提交版本

#include<iostream>
#include<vector>
#include<memory.h>
#include<stack>
#include<algorithm>
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
#define G 6
#define H 7
#define I 8
#define L 9
#define M 10
#define N 11
#define O 12
#define P 13
#define R 14
#define S 15
#define T 16
#define U 17
#define V 18
#define Z 19
​
using namespace std;
​
int h[20] =
{ 366,0,160,242,161,
178,77,151,226,244,
241,234,380,98,193,
253,329,80,199,374 };
​
struct node
{
    int g;
    int h;
    int f;
    int name;
    node(int name, int g, int h)
    {
        this->name = name;
        this->g = g;
        this->h = h;
        this->f = g + h;
    };
    bool operator <(const node &a)const
    {
        return f < a.f;
    }
};
​
​
class Graph
{
public:
    Graph()
    {
        memset(graph, -1, sizeof(graph));
    }
    int getEdge(int from, int to)
    {
        return graph[from][to];
    }
    void addEdge(int from, int to, int cost)
    {
        if (from >= 20 || from < 0 || to >= 20 || to < 0)
            return;
        graph[from][to] = cost;
    }
    
    void init(){
        addEdge(O, Z, 71);
        addEdge(Z, O, 71);
​
        addEdge(O, S, 151);
        addEdge(S, O, 151);
​
        addEdge(Z, A, 75);
        addEdge(A, Z, 75);
​
        addEdge(A, S, 140);
        addEdge(S, A, 140);
​
        addEdge(A, T, 118);
        addEdge(T, A, 118);
​
        addEdge(T, L, 111);
        addEdge(L, T, 111);
​
        addEdge(L, M, 70);
        addEdge(M, L, 70);
​
        addEdge(M, D, 75);
        addEdge(D, M, 75);
​
        addEdge(D, C, 120);
        addEdge(C, D, 120);
​
        addEdge(C, R, 146);
        addEdge(R, C, 146);
​
        addEdge(S, R, 80);
        addEdge(R, S, 80);
​
        addEdge(S, F, 99);
        addEdge(F, S, 99);
​
        addEdge(F, B, 211);
        addEdge(B, F, 211);
​
        addEdge(P, C, 138);
        addEdge(C, P, 138);
​
        addEdge(R, P, 97);
        addEdge(P, R, 97);
​
        addEdge(P, B, 101);
        addEdge(B, P, 101);
​
        addEdge(B, G, 90);
        addEdge(G, B, 90);
​
        addEdge(B, U, 85);
        addEdge(U, B, 85);
​
        addEdge(U, H, 98);
        addEdge(H, U, 98);
​
        addEdge(H, E, 86);
        addEdge(E, H, 86);
​
        addEdge(U, V, 142);
        addEdge(V, U, 142);
​
        addEdge(I, V, 92);
        addEdge(V, I, 92);
​
        addEdge(I, N, 87);
        addEdge(N, I, 87);
    }
​
private:
    int graph[20][20];
};
​
bool list[20]; //记录节点是否在当前队列中
vector<node> openList;//扩展队列
bool closeList[20];//记录节点是否被扩展(选择)过
stack<int> road;//记录路径
int parent[20];//记录路径
​
void A_star(int goal,node &src,Graph &graph)
{
    openList.push_back(src); //起点入队
    sort(openList.begin(), openList.end());
    
    while (!openList.empty())//队列中还有可扩展节点,就不断扩展
    {
        /********** Begin **********/
        node n=openList[0];
        if(n.name==goal) return; //扩展节点是目标节点,搜索结束
        openList.erase(openList.begin());
        closeList[n.name]=1; //当前节点已扩展
        list[n.name]=0; //当前节点出队
        for(int i=0;i<20;i++){
            if(graph.getEdge(n.name,i)!=-1 && closeList[i]!=1){
                int cost=n.g+graph.getEdge(n.name,i);//到下一个城市的代价
                if(list[i]){
                    //更新已有节点
                    for(int j=0;j<openList.size();j++){
                        if(openList[j].name==i){
                            if(openList[j].g>cost){
                                openList[j].g=cost;
                                openList[j].f=cost+openList[j].h;
                                parent[i]=n.name;
                            }
                            break;
                        }
                    }
                }
                else{
                    //构造新节点
                    node newNode(i,cost,h[i]);
                    openList.push_back(newNode);
                    list[i]=1;
                    parent[i]=n.name;
                }
            }
        }
        sort(openList.begin(),openList.end());  
        /********** End **********/  
    }
}
​
void print_result(Graph &graph)
{
    int p = openList[0].name;
    int lastNodeNum;
    road.push(p);
    while (parent[p] != -1)
    {
        road.push(parent[p]);
        p = parent[p];
    }
    lastNodeNum = road.top();
    int cost = 0;
    cout << "solution: ";
    while (!road.empty())
    {
        cout << road.top() << "-> ";
        if (road.top() != lastNodeNum)
        {
            cost += graph.getEdge(lastNodeNum, road.top());
            lastNodeNum = road.top();
        }
        road.pop();
    }
    cout << "end" << endl;
    cout << "cost:" << cost;
}

2.测试版本

#include<iostream>
#include<vector>
#include<memory.h>
#include<stack>
#include<algorithm>
​
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
#define G 6
#define H 7
#define I 8
#define L 9
#define M 10
#define N 11
#define O 12
#define P 13
#define R 14
#define S 15
#define T 16
#define U 17
#define V 18
#define Z 19
​
using namespace std;
string ch="ABCDEFGHILMNOPRSTUVZ";
​
​
​
int h[20] =//从n节点到目标节点可能的最优路径的估计代价
{ 366,0,160,242,161,
178,77,151,226,244,
241,234,380,98,193,
253,329,80,199,374 };
​
​
/*
*一个节点结构,node
*/
struct node
{
    int g;                              //从起始节点到n节点的已走过路径的实际代价
    int h;                              //从n节点到目标节点可能的最优路径的估计代价
    int f;                              //代价估计函数
    int name;
    node(int name, int g, int h) {       //构造函数
        this->name = name;
        this->g = g;
        this->h = h;
        this->f = g + h;
    };
​
    //重载运算符
    bool operator <(const node& a)const { return f < a.f; }
};
​
​
class Graph        //图结构
{
public:
    Graph() {
        memset(graph, -1, sizeof(graph));   //图初始化为-1,代表无边
    }
​
    int getEdge(int from, int to) {       //获取边的开销
        return graph[from][to];
    }
​
    void addEdge(int from, int to, int cost) {    //新增一条边及其开销
        if (from >= 20 || from < 0 || to >= 20 || to < 0)
            return;
        graph[from][to] = cost;
    }
​
    void init() {                        //图初始化
        addEdge(O, Z, 71);
        addEdge(Z, O, 71);
​
        addEdge(O, S, 151);
        addEdge(S, O, 151);
​
        addEdge(Z, A, 75);
        addEdge(A, Z, 75);
​
        addEdge(A, S, 140);
        addEdge(S, A, 140);
​
        addEdge(A, T, 118);
        addEdge(T, A, 118);
​
        addEdge(T, L, 111);
        addEdge(L, T, 111);
​
        addEdge(L, M, 70);
        addEdge(M, L, 70);
​
        addEdge(M, D, 75);
        addEdge(D, M, 75);
​
        addEdge(D, C, 120);
        addEdge(C, D, 120);
​
        addEdge(C, R, 146);
        addEdge(R, C, 146);
​
        addEdge(S, R, 80);
        addEdge(R, S, 80);
​
        addEdge(S, F, 99);
        addEdge(F, S, 99);
​
        addEdge(F, B, 211);
        addEdge(B, F, 211);
​
        addEdge(P, C, 138);
        addEdge(C, P, 138);
​
        addEdge(R, P, 97);
        addEdge(P, R, 97);
​
        addEdge(P, B, 101);
        addEdge(B, P, 101);
​
        addEdge(B, G, 90);
        addEdge(G, B, 90);
​
        addEdge(B, U, 85);
        addEdge(U, B, 85);
​
        addEdge(U, H, 98);
        addEdge(H, U, 98);
​
        addEdge(H, E, 86);
        addEdge(E, H, 86);
​
        addEdge(U, V, 142);
        addEdge(V, U, 142);
​
        addEdge(I, V, 92);
        addEdge(V, I, 92);
​
        addEdge(I, N, 87);
        addEdge(N, I, 87);
    }
​
private:
    int graph[20][20];              //图数组,用来保存图信息,最多有20个节点
};
​
bool list[20];                          //用于记录节点i是否在openList集合中
vector<node> openList;                  //扩展节点集合
bool closeList[20];                     //已访问节点集合
stack<int> road;                        //路径
int parent[20];                         //父节点,用于回溯构造路径
​
void A_star(int goal, node& src, Graph& graph)//A*搜索算法
{
    openList.push_back(src);                    //扩展集合加入起始节点
    sort(openList.begin(), openList.end());     //排序扩展集合的节点,以取出代价最小的节点
    for(auto x:openList) cout<<x.name<<" ";     cout<<endl; 
​
    while (!openList.empty())
    {
        /********** Begin **********/
        node curNode = openList[0];             //取出扩展集合第一个节点,即代价最小的节点        
        cout<<"当前扩展节点:"<<ch[curNode.name]<<endl;
        if (curNode.name == goal) {             //如果当前节点就是目标节点,则退出
            cout<<"\t当前扩展节点为GOAL"<<endl;
            return;
        }
        openList.erase(openList.begin());       //将当前节点从扩展列表中删除
        closeList[curNode.name] = true;         //将当前节点加入已访问节点
        list[curNode.name] = false;             //标记当前节点已不在扩展集合中
​
        for (int i = 0; i < 20; i++) {                      //开始扩展当前节点,即找到其邻居节点
            if (graph.getEdge(i, curNode.name) == -1) {     //若不是当前节点的邻居节点,跳到下一个节点
                continue;
            }
            if (closeList[i]) {                             //若此节点已加入已访问集合closeList,也跳到下一个节点
                cout<<"\t节点--"<<ch[i]<<" 已访问过"<<endl; 
                continue;
            }
            cout<<"\t节点--"<<ch[i]<<" 未访问过,且与当前扩展节点--"<<ch[curNode.name]<<"有边相连"<<endl;
            int g1 = curNode.g + graph.getEdge(i, curNode.name);    //计算起始节点到当前节点i的g值
            int h1 = h[i];                                          //获得当前节点i的h值
            if (list[i]) {                                          //如果节点i在openList中
                cout<<"\t\t节点--"<<ch[i]<<" 在扩展节点集合中"<<endl; 
                for (int j = 0; j < openList.size(); j++) {
                    if (i == openList[j].name) {                    //首先找到节点i的位置,即j
                        if (g1 < openList[j].g) {                   //如果新的路径的花销更小,则更新
                            cout<<"\t\t\t新路径花销更小,更新"<<endl; 
                            openList[j].g = g1;
                            openList[j].f = g1 + openList[j].h;
                            parent[i] = curNode.name;               //记录父节点
                            break;
                        }
                        cout<<"\t\t\t新路径花销不会更优,不更新"<<endl; 
                    }
                }
            }
            else {                        //如果节点i不在openList,则将其加入其中(因为扩展时访问了它)
                cout<<"\t\t节点--"<<ch[i]<<" 不在扩展节点集合中"<<endl;
                cout<<"\t\t\t创建新节点,加入扩展队列"<<endl; 
                node newNode(i, g1, h1);                    //创建新节点,其参数已知
                openList.push_back(newNode);                //新节点加入openList中
                parent[i] = curNode.name;                   //记录父节点
                list[i] = true;                             //记录节点i加入了openList
            }
        }
        cout<<"openList: "; for(auto x:openList) cout<<ch[x.name]<<"-"<<x.f<<" ";   cout<<endl;
        sort(openList.begin(), openList.end());     //扩展完当前节点后要对openList重新排序
        cout<<"openList-sorted: "; for(auto x:openList) cout<<ch[x.name]<<"-"<<x.f<<" ";    cout<<endl<<endl;
        /********** End **********/
    }
}
​
void print_result(Graph& graph)     //用于打印路径和开销
{
    cout<<"\nparent数组:"<<endl;
    for(int i=0;i<20;i++) 
    if(parent[i]!=-1) cout<<"节点--"<<ch[i]<<"的父节点是:"<<ch[parent[i]]<<endl; 
    cout<<endl;
    
    int p = openList[0].name;       //p即为目标节点
    int lastNodeNum;
    road.push(p);                   //目标节点压入栈中,之后最后才输出
    while (parent[p] != -1)         //不断回溯获得一条完整路径
    {
        road.push(parent[p]);
        p = parent[p];
    }
    lastNodeNum = road.top();       //起始节点
    int cost = 0;                   //总开销
    cout << "solution: ";
    while (!road.empty())           //栈不为空就继续循环
    {
        cout << road.top() << "-> ";
        if (road.top() != lastNodeNum)  //如果栈顶元素不是终点
        {
            cost += graph.getEdge(lastNodeNum, road.top());     //添加花销
            lastNodeNum = road.top();       //更新栈顶元素
        }
        road.pop();     //弹出栈顶元素
    }
    cout << "end" << endl;
    cout << "cost:" << cost;
}
​
int main()
{
    Graph graph;
    graph.init();
    int goal = B;                           //目标节点B
    node src(A, 0, h[A]);                   //起始节点A
    list[A] = true;
    memset(parent, -1, sizeof(parent));     //初始化parent
    memset(list, false, sizeof(list));      //初始化list
    A_star(goal, src, graph);
    print_result(graph);
    return 0;
}

另:第一章-绪论

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值