Dijkstra算法:
这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。
初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),若存在能直接到达的边(s,m),则把d[m]设为w(s,m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于V 中所有顶点 v 除 s 和上述 m 外 d[v] = ∞)。当算法退出时,d[v] 中存储的便是从s 到 v 的最短路径,或者如果路径不存在的话是无穷大。
•基础操作:
边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u,v)添加到尾部来拓展一条从 s 到 v 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前d[v] 中的值。拓展边的操作一直运行到所有的 d[v] 都代表从 s 到 v 最短路径的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u,v)都只被拓展一次。
算法维护两个顶点集 S 和 Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。
S :源;
p[ ] :记录点属于哪个集合:true表示属于Va;false表示属于Vb;
map[ ][ ]:记录图信息,map[u][v]为点到边的长度;
dist[ ]:保存结果;
pre[ ]: 记录最短路径终点的前驱。
(1) 初始化:源的距离dist[s]设为0,其他点的距离设为map[s][i],p[s]=true,其他各点的p[i] = false;
(2) 循环n – 1次:
I.在Vb中的点中取一s到其距离最小的点k,p[k] =true.如果所有k都不可达,退出循环,算法结束。
II.对于每个与k相邻的在Vb中的点j,更新s到j的最短路径。如果dist[k] +map[k][j] < dist[j],那么dist[j] = dis t[k] + map[k][j]。此时到点j的最短路径上,终点j的前一个节点即为k,即pre[j] = k.
function Dijkstra(G, w, s)
for each vertex v in V[G] // 初始化
d[v] := infinity // 將各點的已知最短距離先設成無窮大
previous[v] := undefined // 各点的已知最短路径上的前趋都未知
d[s] := 0 // 因为出发点到出发点间不需移动任何距离,所以可以直接将s到s的最小距离设为0
S := empty set
Q := set of all vertices
while Q is not an empty set // Dijkstra演算法主體
u := Extract_Min(Q)
S.append(u)
for each edge outgoing from u as (u,v)
if d[v] > d[u] + w(u,v) // 拓展边(u,v)。w(u,v)为从u到v的路径长度。
d[v] := d[u] + w(u,v) // 更新路径长度到更小的那个和值。
previous[v] := u // 紀錄前趨頂點
如果我们只对在 s 和 t 之间查找一条最短路径的话,我们可以在第9行添加条件如果满足 u = t 的话终止程序。
通过推导可知,为了记录最佳路径的轨迹,我们只需记录该路径上每个点的前趋,即可通过迭代来回溯出 s 到 t 的最短路径(当然,使用后继节点来存储亦可。但那需要修改代码):
s := empty sequence
u := t
while defined u
insert u to the beginning of S
u := previous[u] //previous数组即为上文中的p
//Dijkstra
const int maxn = 10001; //点个数
void Dijkstra(int n,int dist[maxn],int map[maxn][maxn],int pre[maxn],int s)
//n个点,dist[i]表示点i到源点s的最短距离,map记录图信息,pre记录前驱、源点、终点
{
int i,j,k;
int min;
bool p[maxn]; //记录该点是否属于Va,不属于Va的点属于Vb
for(i = 1;i <= n; i++)//初始化
{
p[i] = false;
if(i != s)
{
dist[i] = map[s][i];
pre[i] = s;
}
}
dist[s] = 0;
p[s] = true;
for(i = 1;i <= n - 1;i++)//循环n - 1次,求s到其他n - 1个点的最短路径
{
min = INT_MAX;
k = 0;
for(j = 1;j <= n;j++)//在Vb中的点中取一s到其距离最小的点k
{
if(! p[j] && dist[j] < min)
{
min = dist[j];
k = j;
}
}
if(k == 0) return;//如果没有点可以扩展,即剩余的点不可达,返回
p[k] = true; //将k从Vb中除去,加入到Va中
for(j = 1; j <= n;j++)
{
//对于每个与k相邻的在Vb中的点j,更新s到j的最短路径
if(! p[j] && map[k][j] != INT_MAX && dist[j] > dist[k] + map[k][j])
{
dist[j] = dist[k] + map[k][j];
pre[j] = k;
}
}
}
}
void dijkstra(int src){
priority_queue <pii,vector<pii>,greater<pii> > q;
memset(d,-1,sizeof(d));
d[src] = 0;
q.push(make_pair(0,src));
while(!q.empty()){
while(!q.empty() && q.top().first > d[q.top().second]) q.pop();
if(q.empty()) break;
int u = q.top().second;
q.pop();
for(int i = first[u];i != -1;i = next[i]){
if(d[v[i]] == -1 || d[v[i]] > d[u]+w[i]){
d[v[i]] = d[u]+w[i];
q.push(make_pair(d[v[i]],v[i]));
}
}
}
}
畅通工程续
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 27161 Accepted Submission(s): 9803
现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
3 3 0 1 1 0 2 3 1 2 1 0 2 3 1 0 1 1 1 2
2 -1
谢谢陶昱正学长,谢谢维基百科