现在的位置: 首页 > 综合 > 正文

最短路总结

2017年10月13日 ⁄ 综合 ⁄ 共 2045字 ⁄ 字号 评论关闭

一,Dijkstra(迪杰斯特拉算法

       Dijkstra算法是求解单源最短路问题的算法。(非负权边)

       Dijkstra提出,按各个顶点与源点之间路径长度的递增次序,生成源点到各个顶点的最短路径。

       也就是说,先找出距离源点最近的一条路径,再参照它找出距离源点次近的一条路径,以此类推,直到从源点到其他各顶点的最短路径全部找出。

       是一种贪心的思想。时间复杂度为n^2。

      

该算法仅适用于单源的非负权边的最短路问题。

如果存在负权边的话,就会出现错误。为什么?因为该算法是基于贪心思想的。如果存在负权会出现错误。

代码实现比较简单:

const int N=?;
const int INF=0xfffff
int map[N][N];
bool vis[N],dis[N],n;//用来区分

void Dijkstra(int s0){
    memset(vis,false,sizeof(vis));
    vis[s0]=true;
    for(int i=1;i<=n;i++)
    dis[i]=map[s0][i];
    //-----------------------------
    for(int i=1;i<=n;i++){
        int Bfind=s0,Dis=INF
        for(int j=1;j<=n;j++)
        if(!vis[j]&&dis[j]<Dis){
            Bfind=j;
            Dis=dis[j];
        }
        if(Bfind==s0) break;
        vis[Bfind]=true;
    //-------------------------------
        for(int j=1;j<=n;j++)
        if(!vis[j]&&dis[j]<dis[Bfind]+map[Bfind][j]){
            dis[j]=dis[Bfind]+map[Bfind][j];
        }
    //-------------------------------
    }
    printf("%d\n",dis[?]);
}

二,Bellman_ford(贝尔曼-福德算法)

        Bellman_ford算法是求解一般单源最短路的算法。(可能存在负边)

        算法操作就是,对图进行|V|-1次松弛操作,即可求出最短路径。

        可以看出Bellman_ford的时间复杂度为|V||E|。

        最重要的是理解什么是松弛操作:

     理解了什么是松弛操作了,理解起来就很容易了。

    为什么要进行|V|-1次松弛操作?

    因为,某点到源点的距离最短,最多通过|V|-1次松弛操作可以得到。

代码很好写:

const int N=?;
const int M=?;
const int INF=0xffffff;
struct Edge{
    int start,end;
    int value;
}edge[M];
int n,m,dis[N],path[N];

void Bellman_ford(){
    for(int i=1;i<=n;i++)
    dis[i]=INF;
    dis[startpoint]=0;
    for(int i=1;i<n;i++){
        for(int j=1;j<=m;j++){
            int st=edge[j].start;
            int en=edge[j].end;
            int va=edge[j].value;
            if(dis[en]>dis[st]+va){
                dis[en]=dis[st]+va;
                path[en]=st;
            }
        }
    }
}

如果存在负权回路,那么,那么就不存在最短路,以为,每次经过负权回路都会减小。

那么,怎么判断是否存在负权回路呢?如果存在负权回路,那么,在进行|V|次以后的松弛操作,还会变小。

所以,只需要在,做完松弛操作后,判断一下,再次做松弛操作时,会不会再次减小。

代码:(加到,松弛完以后)

bool Judge(){
    for(int i=1;i<=m;i++){
        int st=edge[j].start;
        int en=edge[j].end;
        int va=edge[j].value;
        if(dis[en]>dis[st]+va)
        return false;
    }
}

打印路径:

需要注意的是,打印路径的时候。需要注意的是,记录的是前驱,而非后继,为什么呢?

因为,一个点最短路径的前驱是唯一的,而后继可能有很多。

递归打印路径,自己写出来的竟然。自己递归还需努力。

int path[N]={'-1'};
int Print_Path(int step){
    if(path[step]==-1) return step;
    else Print_Path(path[one]);
    printf("%s%d",step==starstep?"":"->"?one);
}

今天的复习就到这里,明天继续。

抱歉!评论已关闭.