图 之 Dijkstra算法(附带习题代码)

2016年11月17日 6点热度 0条评论 来源: fanfan4569

定义概览

  Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。注意该算法要求图中不存在负权边。





我的理解

  从开始顶点出发,先将开始顶点吃入(即标记已访问

    1。找其最短的边所连的顶点

    2。将其吃入(即标记已访问

    3。遍历与其相连的其他顶点

    4。若从V 到W距离小于之前到W的距离,则修改(更新

    5。可以用个堆栈来存储路径

    6。循环1~4 更新距离, 直至所有的顶点都已经吃入

(1)dist 表示从这个点(数组序号)到原点的最短距离,每次也更新这里

(2)path 用来表示路径

(3)注意:第一次是要把入口顶点所在的边吃入先——–这步初始化别忘了

/* 邻接矩阵存储 - 有权图的单源最短路算法 */

Vertex FindMinDist( MGraph Graph, int dist[], int collected[] )
{ /* 返回未被收录顶点中dist最小者 */
    Vertex MinV, V;
    int MinDist = INFINITY;

    for (V=0; V<Graph->Nv; V++) {
        if ( collected[V]==false && dist[V]<MinDist) {
            /* 若V未被收录,且dist[V]更小 */
            MinDist = dist[V]; /* 更新最小距离 */
            MinV = V; /* 更新对应顶点 */
        }
    }
    if (MinDist < INFINITY) /* 若找到最小dist */
        return MinV; /* 返回对应的顶点下标 */
    else return ERROR;  /* 若这样的顶点不存在,返回错误标记 */
}

bool Dijkstra( MGraph Graph, int dist[], int path[], Vertex S )
{
    int collected[MaxVertexNum];
    Vertex V, W;

    /* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
    for ( V=0; V<Graph->Nv; V++ ) {
        dist[V] = Graph->G[S][V];
        if ( dist[V]<INFINITY )
            path[V] = S;
        else
            path[V] = -1;
        collected[V] = false;
    }
    /* 先将起点收入集合 */
    dist[S] = 0;
    collected[S] = true;

    while (1) {
        /* V = 未被收录顶点中dist最小者 */
        V = FindMinDist( Graph, dist, collected );
        if ( V==ERROR ) /* 若这样的V不存在 */
            break;      /* 算法结束 */
        collected[V] = true;  /* 收录V */
        for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
            /* 若W是V的邻接点并且未被收录 */
            if ( collected[W]==false && Graph->G[V][W]<INFINITY ) {
                if ( Graph->G[V][W]<0 ) /* 若有负边 */
                    return false; /* 不能正确解决,返回错误标记 */
                /* 若收录V使得dist[W]变小 */
                if ( dist[V]+Graph->G[V][W] < dist[W] ) {
                    dist[W] = dist[V]+Graph->G[V][W]; /* 更新dist[W] */
                    path[W] = V; /* 更新S到W的路径 */
                }
            }
    } /* while结束*/
    return true; /* 算法执行完毕,返回正确标记 */
}

题目链接:https://pta.patest.cn/pta/test/1342/exam/4/question/23160

坑点:

  高速公路是双方向的。一开始我以为是单方向的,然后就只过了一个用例。第一次练这题,所以有点啰嗦,请见谅!

//07-图6 旅游规划 (25分)

#include<cstdio>

using namespace std;
#define MAXN 505
#define INFINITY 505

struct City{
    int len;
    int fees;
};

int N;  //城市的个数 编号 0 ~ (N - 1)
int M;  //高速公路的条数
int S;  //出发地的城市编号
int D;  //目的城市编号
City city[MAXN][MAXN];  //图
int flag[MAXN]; //标记
City dist[MAXN];   //dist表示 这个点到原点的最短路径
int Length;    //路径长度
int Fees;  //收费额

void init(){
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < N; ++j){
            city[i][j].len = INFINITY;
            city[i][j].fees = INFINITY;
        }
    }
    for(int i = 0; i < N; ++i){
        dist[i].len = INFINITY;
        dist[i].fees = INFINITY;
    }
}

void setDistValue(int s, int i, int j){
    dist[S].len = i;
    dist[S].fees = j;
}

int findMinDist(){
    City minDist;
    minDist.fees = INFINITY;
    minDist.len = INFINITY;

    int V;  //用于返回的顶点

    for(int i = 0; i < N; ++i){
        if(flag[i] == 0){
            if(dist[i].len < minDist.len){
                minDist.len = dist[i].len;
                minDist.fees = dist[i].fees;
                V = i;
            }else if(dist[i].len == minDist.len){
                if(dist[i].fees < minDist.fees)
                    minDist.fees = dist[i].fees;
            }
        }
    }
    if(minDist.len < INFINITY)
        return V;       //返回对应的顶点下标
    else    return -1;  //这样的顶点不存在,返回错误标记
}

void dijkstra(){
    setDistValue(S, 0, 0);  //将起点吃入集合
    flag[S] = 1;            //标记
    for(int i = 0 ;i < N; ++i){
        dist[i].len = city[S][i].len;
        dist[i].fees = city[S][i].fees;
    }
    int V;                  //用来表示顶点下标
    while(1){
        V = findMinDist();
        if(V == -1)     //这样结点不存在
            break;
        flag[V] = 1;    //吃入
        for(int i = 0; i < N; ++i){ //对图中的每个顶点
            if(flag[i] == 0 && city[V][i].len < INFINITY){  // W是V的邻边且未被吃入
                if(city[V][i].len < 0) //为负边
                    return ;    //不能正确处理,返回错误标记
                if(dist[V].len + city[V][i].len < dist[i].len){ //吃入V使得dist[i]变小
                    dist[i].len = dist[V].len + city[V][i].len;
                    dist[i].fees = dist[V].fees + city[V][i].fees;
                }else if(dist[V].len + city[V][i].len == dist[i].len){ //吃入V等于dist[i]
                    if(dist[V].fees + city[V][i].fees < dist[i].fees)   //路费比其少则更新
                        dist[i].fees = dist[V].fees + city[V][i].fees;
                }

            }
        }
    }
}

int main(void){
    scanf("%d%d%d%d", &N, &M, &S, &D);
    init(); //初始化
    int beginCity;
    int endCity;
    int len;
    int fees;
    for(int i = 0; i < M; ++i){
        scanf("%d%d%d%d", &beginCity, &endCity, &len, &fees);
        city[beginCity][endCity].len = len;
        city[beginCity][endCity].fees = fees;
        city[endCity][beginCity].len = len;
        city[endCity][beginCity].fees = fees;
    }
    dijkstra();
// for(int i = 0 ; i < N; ++i)
// printf("i=%d len=%d fees=%d\n", i, dist[i].len, dist[i].fees);
    printf("%d %d", dist[D].len, dist[D].fees);

    return 0;
}
    原文作者:fanfan4569
    原文地址: https://blog.csdn.net/fanfan4569/article/details/53206486
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。