(说实话,今天实验室的人去听宣讲会不和我说。之前问也不说在干什么,我很生气。不过算他们错过了一个‘人才’吧)
dijkstra算法概论:该算法应用在图论里面著名的 单源最短路径问题。
算法适用性:圣经 黑书指出,该算法应用在 无负权,有向图中。
算法复杂度:事实上,黑书是在图割、轻边的概念上展开算法。黑书维护了一个最小优先队列(以到d值排序),作为 V - S。 初始化后,每一次迭代(v 次)都对队列值最小的节点 的每一条出边进行松弛操作。因此维护队列 涉及到 插入(初始化),弹出(最小值),以及重排序(松弛操作)。前两者对于每一个节点都是一次操作,而重排序(堆的下沉操作)对每一个节点的每一条出边都要进行操作,所以是e次重排序。
若 按照 白书的代码 来实现(简单的数组操作,而不是最小二叉堆),则需要 V^2+E的复杂度。
仍然可以改进。加上稀疏图和最小二叉堆实现可以降低到,V^2/lgV.。
如果采用非波那契堆来实现,则可以降低到,VlgV+E。
(先回忆到这里,待续)(Linux下的编辑真是难看,我们继续)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
刚刚出去找到了 POJ 1052,这个题目 直接是DAG单源最短路径问题,而且用最慢的Dj实现也通过了。代码如下,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define INF 0xfffffff
#define MAXN 105
int d[MAXN];
int w[MAXN][MAXN];
int v[MAXN];
int n;
void dj(int s){
memset(v, 0, sizeof(v));
for(int i = 0; i < n; i++)
d[i] = ( i == s ? 0 : INF );
for(int i = 0; i < n; i++){
int x, m = INF;
for(int y = 0; y < n; y++)
if( !v[y] && d[y] <= m )
m = d[x=y];
v[x] = 1;
for(int y = 0; y < n; y++)
if( w[x][y] != -1 && d[y] > d[x] + w[x][y])
d[y] = d[x] + w[x][y];
}
}
int s2i(char* str){
if (str[0] == 'x') return -1;
int ret = 0;
while (*str)
ret = ret * 10 + (*str++) - '0';
return ret;
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
w[i][j] = -1;
char input[40];
for(int i = 0; i < n; i++){
w[i][i] = 0;
for(int j = 0; j < i; j++){
scanf("%s", input);
w[i][j] = w[j][i] = s2i(input);
}
}
dj(0);
int output = -1;
for(int i = 1; i < n; i++)
if( d[i] > output ) output = d[i];
printf("%d\n", output);
return 0;
}
关键算法在 dj 函数,采用数组实现最小优先队列(直接暴力遍历数组)找出最近的节点(轻边)。然后对 该节点所有 出边 进行松弛操作。每一次找到的(最近节点)都要出列,这里通过访问标签来实现。事实上,在松弛操作的时候也可以维护祖先节点,进而在最后得到路径上的节点序列。
POJ 评判的耗时是0ms 所以 对它改进也没有意义(这一题 估计就是给新手练习的)。
(我去找一个 有必要 改进队列实现的题目来,待续)
--------------------------------------------------------------------------------------------------------------------------------------------------------------