问题描述
感谢@trincot的codes,我可以修改Dijkstra以获得给定源节点和目标节点之间的最短路径。 而且,我尝试执行Dijkstra来找到最短路径时对跳数进行计数,当跳数超过预定义的Max_hop时,Dijkstra将终止,但失败了。
跳数定义为(N-1),其中N是最短路径中包含的顶点数。
当然,找到最短路径后,我们可以轻松计算跳数。但是,在Dijkstra的路径搜索过程中,我们可以计算给定源与之间的跳数吗?
from heapq import heappop,heappush
def dijkstra(adjList,source,sink):
n = len(adjList)
parent = [None]*n
heap = [(0,0)]
explored_node=[]
hop_count = 0
Max_hop = 8
while heap:
distance,current,came_from = heappop(heap)
if parent[current] is not None: # skip if already visited
continue
parent[current] = came_from # this also marks the node as visited
if sink and current == sink: # only correct place to have terminating condition
# build path
path = [current]
while current != source:
current = parent[current]
path.append(current)
path.reverse()
hop_count -=1
print("Hop count is ",hop_count)
return 1,distance,path
for (neighbor,cost) in adjList[current]:
if parent[neighbor] is None: # not yet visited
heappush(heap,(distance + cost,neighbor,current))
hop_count = hop_count + 1
if hop_count > Max_hop:
print("Terminate")
adjList =[
[],[[2,3],[4,11],[5,5]],[[1,[3,5],[6,7]],3]],15],[7,9]],[2,6],[8,[9,7],10]],[[4,9],1],[10,[11,8]],[[5,11]],10],[12,[[7,[13,[14,8],6]],[[9,[15,[[10,[[12,]
flag,dist,path = dijkstra(adjList,1,15)
print("found shortest path {},which has a distance of {}".format(path,dist))
adjList的图形如下所示:(红线是从1到15的最短路径)
我知道这是不正确的,因为当Dijkstra迭代邻居时,我使hop_cout + 1
代表探索的节点数而不是hop_count。
我认为,有两个重要的问题需要解决。
- 确定了parent_node和neighbor_node之间的最短距离后,可以将hop_count加1。但是,Dijkstra通过迭代邻居节点来找到最短路径,并且在路径搜索期间逐渐更新了存储最短距离的数组。 。
How to determine Dijkstra has already found the shortest distance between a parent_node and a neighbor_node?
- 仅条件1是不够的,即使我们可以知道何时Dijkstra找到了两个节点之间的最短距离,但是如何知道邻居节点是否将包含在给定源与目的地之间的最短路径中?
总而言之,如果我们想知道Dijkstra运行时的当前跳数,则需要设置hop_count +1,当确定了从parent_node到neighbor_node的最短路径,并将neighbor_node包括到从源到目标节点的最短路径。
为了更好地定义问题,如图所示,红线是node 1
和node 15
之间的最短路径,最短路径是1 ->5 ->8 ->7 ->10 ->13 ->15
。
- 探索
node 2
时,node 1
与node 2
被确定为3,hop_count
不能加1,因为node 2
不在1到15之间的最短路径中。 - 探索
node 5
时,node 1
与node 5
被确定为5,hop_count
应加1 ,因为node 5
包含在1到15之间的最短路径中。
我的理解正确吗?我是否可以听到您的想法,即“执行Dijkstra时是否可以确定跳数?”
解决方法
由于堆中的节点表示路径的长度是变化的,因此您不能希望将一个变量用于跃点计数。您需要将跳数作为附加信息添加到您放在堆中的元组中,因为它是特定于每个单独路径的。
注意:我也将max_hop
设为函数的参数:
from heapq import heappop,heappush
def dijkstra(adjList,source,sink,max_hop=8): # make max_hop a parameter
n = len(adjList)
parent = [None]*n
heap = [(0,0)] # added hop_count as 4th value
hop_count = 0
while heap:
distance,current,came_from,hop_count = heappop(heap) # get hop_count also
if parent[current] is not None:
continue
parent[current] = came_from
if sink and current == sink:
path = [current]
while current != source:
current = parent[current]
path.append(current)
path.reverse()
print("Hop count is ",hop_count)
return 1,distance,path
if hop_count >= max_hop: # no recursion beyond max_hop
print("Terminate")
continue
for (neighbor,cost) in adjList[current]:
if parent[neighbor] is None:
# increase hop_count on this particular path
heappush(heap,(distance + cost,neighbor,hop_count + 1))
关于您的其他问题:
如何确定Dijkstra已经找到父节点和邻居节点之间的最短距离?
这是if
循环中的for
所检测到的:如果该节点已经被访问过,则意味着它已经在堆上获得了优先级,并在先前的迭代中被从该节点中拉出。主while
循环,因此我们已经有了到该节点的最短路径。 if
阻止我们在堆上推送无用的“替代”路径。
这里有两个问题,一个是如何跟踪路径的长度,另一个是一旦超过最大路径长度就终止程序。两者都有完全不同的答案。
一方面,您可以通过在算法完成后获取路径的长度来计算最短路径的跳数(尽管这似乎不是您想要的)。其次,您还可以跟踪在任意迭代中从源到任何给定节点X需要多少跳,只需跟踪从s到顶点X的当前路径的长度并更新路径长度放松步骤的邻居。 @trincot答案也提供了代码。
现在,在进入程序终止部分之前,我先介绍三个有用的引理,这些引理通过Dijkstra算法是不变的。
引理1:对于每个标记的顶点,从源到该顶点的距离是最短的路径。
引理2:对于每个未标记的顶点,当前记录的距离是仅考虑已经访问过的顶点的最短路径。
引理3:如果最短的是s-> ...-> u-> v那么,当u被访问并且邻居的距离被更新时,距离d(s,v)将保持不变。
这些引理告诉我们的是:
- 当节点X标记为已访问时,则d(s,x)最小,路径s-> x的长度将保持不变(引理1起)
- 直到节点X被标记为已访问d(s,x)是一个估计,并且路径s-> x的长度是当前路径的长度。这两个值都可能改变。 (引自Lemma 2)
- 您不能保证长度为N的路径是最短路径,也不能保证最短路径的长度为
因此,如果您决定在从源到接收器的路径长度大于最大跃点数时终止程序,则不能保证所获得的信息是最佳的。特别是,其中任何一种都可能在程序终止时发生:
- 路径长度为N,但是还有另一条长度为N的路径,距离较短。
- 路径长度为N,还有另一条路径长度较短且距离较短。
如果要在限制路径长度的同时获得从源到宿的最短路径,则应改用Bellman-Ford算法,该算法可确保在每次迭代i
时所有路径都具有长度最多i
条边,并且该路径在该约束条件下最短。
此代码将优先队列用于dijkstra算法。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cstdio>
#include <vector>
#define limit 15
using namespace std;
int cost[20001];
vector<int> plist[20001];
const int MaxVal = -1;
vector< vector< pair<int,int> > > arr;
struct node {
pair<int,int> info;
vector<int> path;
};
bool operator < (node a,node b) {
return a.info.first > b.info.first;
}
int main() {
int i,j,k;
int n,m;
int s;
int a,b,c;
cin >> n >> m;
cin >> s;
//arr.reserve(n + 1);
arr.resize(n + 1);
for (i = 1; i <= m; i++) {
cin >> a >> b >> c;
arr[a].push_back({ b,c });
}
for (i = 1; i <= n; i++) {
cost[i] = MaxVal;
}
priority_queue<node,vector<node>> mh;
mh.push(node{ { 0,s },{ } });
while (mh.size() > 0) {
int current = mh.top().info.second;
int val = mh.top().info.first;
auto path = mh.top().path;
mh.pop();
if (cost[current] != MaxVal) continue;//All path would be sorted in prioirty queue. And the path that got out late can't be the shorter path.
cost[current] = val;
path.push_back(current);
if(path.size() > limit) {
//limit exceeded!!
cout << "limitation exceeded!!";
break;
}
plist[current] = path;
for (auto it : arr[current]) {
if (cost[it.first] != MaxVal) continue;
mh.push({ { it.second + val,it.first },path });
}
}
for (i = 1; i <= n; i++) {
cout << "path to " << i << " costs ";
if (cost[i] == MaxVal) {
cout << "INF\n";
}
else {
cout << cost[i] << "\n";
}
for (auto p : plist[i]) {
cout << p << " ";
}
cout << endl << endl;
}
return 0;
}
//test case
15 55
1 //Starting Node Number
1 2 3
1 4 11
1 5 5
2 1 3
2 3 5
2 5 11
2 6 7
3 2 5
3 6 3
4 1 11
4 5 15
4 7 9
5 1 5
5 2 11
5 6 3
5 7 6
5 8 3
5 9 9
6 2 7
6 3 3
6 5 3
6 9 10
7 4 9
7 5 6
7 8 1
7 10 11
7 11 8
8 5 3
8 7 1
8 9 9
8 11 11
9 5 9
9 6 10
9 8 9
9 11 3
9 12 8
10 7 11
10 13 7
10 14 3
11 7 8
11 8 11
11 9 3
11 12 8
11 14 6
12 9 8
12 11 8
12 15 11
13 10 7
13 15 3
14 10 3
14 11 6
14 15 9
15 12 11
15 13 3
15 14 9
path to 1 costs 0
1
path to 2 costs 3
1 2
path to 3 costs 8
1 2 3
path to 4 costs 11
1 4
path to 5 costs 5
1 5
path to 6 costs 8
1 5 6
path to 7 costs 9
1 5 8 7
path to 8 costs 8
1 5 8
path to 9 costs 14
1 5 9
path to 10 costs 20
1 5 8 7 10
path to 11 costs 17
1 5 8 7 11
path to 12 costs 22
1 5 9 12
path to 13 costs 27
1 5 8 7 10 13
path to 14 costs 23
1 5 8 7 11 14
path to 15 costs 30
1 5 8 7 10 13 15