📜  门| GATE-CS-2005 |问题 12(1)

📅  最后修改于: 2023-12-03 14:58:26.807000             🧑  作者: Mango

问题 12

这是一道关于最短路径算法的问题。

已知一个带权有向图,起点为 $s$,终点为 $t$。给定每个节点的估计值 $h(v)$,其中 $h(t) = 0$。设从起点 $s$ 到每个节点 $v$ 的最短路径长度为 $g(v)$。则有 $f(v) = g(v) + h(v)$。

现在有一个 A* 算法的实现,但效率很低。你需要利用已有代码,修复该算法,使其运行效率更高。

代码实现

下面是该 A* 算法的实现代码:

 void a_star(Graph graph, int s, int t) {
     PriorityQueue<Node> pq = new PriorityQueue<>();
     pq.add(new Node(s, 0));
 
     while (!pq.isEmpty()) {
         Node node = pq.poll();
         int u = node.u;
 
         if (u == t) {
             System.out.println(node.d);
             return;
         }
 
         for (int v = 0; v < graph.n; v++) {
             if (graph.edges[u][v] > 0) {
                 int d = node.d + graph.edges[u][v];
                 if (d < g[v]) {
                     g[v] = d;
                     f[v] = d + h[v];
                     pq.add(new Node(v, f[v]));
                 }
             }
         }
     }
 }

其中,Graph 表示图的邻接矩阵,Node 表示一个节点,u 是节点编号,d 是起点到该节点的距离,g 是起点到每个节点的最短距离,f 是 $f(v) = g(v) + h(v)$ 的值,h 表示每个节点的估计值。

你需要做什么

请修改以上代码中的错误,使得该 A* 算法的效率更高。你需要在以下代码空白处填写正确的代码或代码块。

void a_star(Graph graph, int s, int t) {
    PriorityQueue<Node> pq = new PriorityQueue<>();
    pq.add(new Node(s, 0));
    g[s] = 0;

    while (!pq.isEmpty()) {
        Node node = pq.poll();
        int u = node.u;
        if (u == t) {
            System.out.println(node.d);
            return;
        }

        for (int v = 0; v < graph.n; v++) {
            if (graph.edges[u][v] > 0) {
                int d = node.d + graph.edges[u][v];
                if (d < g[v]) {
                    g[v] = d;
                    f[v] = d + h[v];
                    pq.add(new Node(v, f[v]));
                }
            }
        }
    }
}
解题思路

该 A* 算法的实现中,存在以下问题:

  1. 未将起点 $s$ 的 $g$ 值初始化为 0。
  2. 每次从优先队列中取出节点后,需要判断该节点是否已经被访问过,以免出现重复计算的情况。这可以通过在节点对象 Node 中添加一个 visited 属性来实现。具体来说,每次往队列中添加节点时,需要将该节点的 visited 属性设置为 true,在取出节点时,先判断该节点是否已经被访问过,如果已经访问过,则跳过该节点,继续取出队列中的下一个节点即可。
  3. 该算法中没有进行路径的重构,所以虽然计算出了最短路径的长度,但无法得到具体路径。可以通过在节点对象 Node 中添加一个 parent 属性来实现。具体来说,每次往队列中添加节点时,需要将该节点的 parent 属性设置为本次取出的节点 node,在打印最短路径长度时,需要从终点 $t$ 开始,依次倒退回起点,找到具体的单源最短路径,最后将该路径反转即可。