美文网首页
——Dijkstra

——Dijkstra

作者: laochonger | 来源:发表于2018-03-11 13:52 被阅读0次

dijkstra由于是贪心的,每次都找一个距源点最近的点(dmin),然后将该距离定为这个点到源点的最短路径。但如果存在负权边,那就有可能先通过并不是距源点最近的一个次优点(dmin'),再通过这个负权边L(L<0),使得路径之和更小(dmin'+L<dmin),则dmin'+L成为最短路径,并不是dmin,这样dijkstra就被囧掉了。
比如n=3,邻接矩阵:
0,3,4
3,0,-2
4,-2,0
用dijkstra求得d[1,2]=3,事实上d[1,2]=2,就是通过了1-3-2使得路径减小。

寻找单源最短路,且边权为正(SSSP)(一个源点到各个顶点)

每次找到离源点最近的一个顶点(除了已经被标记过之外的),然后以该顶点为中心进行扩展,最终得到远点到其余所有店的最短路径。

for(int i = 1; i < n; i++){//n-1次 
    min = inf;
    for(int j = 1; j <= n; j++){
        if(book[j] == 0 && dis[j] < min){
            min = dis[j];//dis保存距离(离源点) 
            u = j;//u为离源点最近的顶点 
        }
        book[u] = 1;//标记,走过的点不会再是后面点的中转 
        for(int k = 1; k <= n; k++){
            if(dis[k] < inf){//概念上防止无穷与无穷相比较 
                if(dis[k] > dis[u] + e[u][k])//如果遍历到的点原来的路程大于中转路程 
                    dis[k] = dis[u] + e[u][k];//将该点原来的路程更新 
            }
        }
    }
}

两版的区别在于是否先为d数组赋初值
d保存最短路径(源点到各个点各个点)
v保存标记

const int INF = 999999999; 
int main(){
    memset( v , 0 , sizeof(v));  //初始化标记数组 
    for(int i = 0; i < n; i++) d[i] = (i == 0? 0 : INF);  //初始化路径大小,设源点到其他点的路长均为INF 
    for(int i = 0; i < n; i++){//一次循环找到一个点 
        int x, m = INF;      // 声明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++) //对于从x出发的所有边(x,y)
                     d[y] = min(d[y] , d[x] + w[x][y]); //更新源点到该点的距离 
    }
    
    return 0;
}

对于这个算法,一开始理解得很模糊,后来又以为是按照一维一维的顺序一直搜索下去,但实际上算法很简单,只要按照d的大小进行遍历就可以,并不存在其他的因素,最外层循环仅仅确定层数,并不作为索引
下面是用优先队列优化后的算法:

const int INF = 0x3f3f3f3f;
const int MAXN=1000010;
struct qnode      //优先队列的创建(储存节点v以及到源点的距离C)
{
    int v;//节点v
    int c;//源点的距离(用来排序)
    qnode(int _v=0, int _c=0):v(_v),c(_c){}  //初始化为0???
    bool operator<(const qnode &r)const//定义优先队列的排序方式(实际上可以用pair进行替代),先比较第一维,相等时比较第二维
    {
        return c>r.c;
    }
};

struct Edge//边结构
{
    int v,cost;    //储存边以及权  
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}//初始化???
};
vector<Edge>E[MAXN];//看作二维数组,每个i数组储存着从i边到下一边的所有数据
bool vis[MAXN];//标记数组
int dist[MAXN];//离源点的距离
void Dijkstra(int n, int strat)     //点的编号从1开始,n是点的个数,start是源点
{
    memset(vis, false, sizeof(vis));//初始化标记数组
    for(int i = 1; i <= n;i++)dist[i] = INF;//初始化距离数组
    priority_queue<qnode>que;//创建定义的优先队列,类型为qnote
    while(!que.empty()) que.pop();//清空队列(这里不明白为什么要清空,是赋了初值吗?那为什么要赋初值)
    dist[start]=0;//源点到源点的距离初始化为0,以便于得出后续
    que.push(qnode(start,0));//源点入队,距离为0
    qnode tmp;//qnode类型的中转变量
    while(!que.empty()){//只要队列不为空,也可以看作每次出队都是一轮松弛
        tmp=que.top();//将队列中d最小的赋给tmp
        que.pop();//出队
        int u = tmp.v;//方便后续的操作,令u=该点的序号
        if(vis[u])continue;//如果已被标记,那么跳过
        vis[u]=true;//标记
        for(int i=0;i<E[u].size();i++){//循环u的下一点的个数次
            int v=E[tmp.v][i].v;//v=(点u数组中的下一点)
            int cost=E[u][i].cost;   //cost = (点u数组中到下一点的距离)
            if(!vis[v]&&dist[v]>dist[u]+cost){ //松弛操作
                dist[v]=dist[u]+cost;
                que.push(qnode(v,dist[v]));//入队
            }
        }
    }
}

void addedge(int u,int v,int w){
    E[u].push_back(Edge(v,w));
}

    Edge(int u, int v, int c, int f):from(u),to(v),cap(c),flow(f){}

这句话的作用是:直接可以使用Edge(1,2,3,4)这样的用法为结构体赋初值(构造函数)

相关文章

  • Dijkstra

    Dijkstra Conception Dijkstra is a single source shortest ...

  • 最短路径模板

    堆优化的Dijkstra 普通Dijkstra SPFA

  • 图算法(二)最短路

    本文将介绍三种常见的最短路算法:Dijkstra,Floyd,SPFA Dijkstra Dijkstra是有向图...

  • 图的最短路径

    Dijkstra算法&Floyd算法 一、Dijkstra算法 Dijkstra算法思想: 只计算v0出发到其他顶...

  • 深入解析Dijkstra's Algorithm ——

    什么是Dijkstra算法? Dijkstra算法是用来寻找最短路径最著名的算法之一。具体来说,Dijkstra算...

  • 算法模板(三)最短路问题

    Dijkstra SPFA Floyd

  • DFS BFS

    BFS DFS Dijkstra

  • ——Dijkstra

    dijkstra由于是贪心的,每次都找一个距源点最近的点(dmin),然后将该距离定为这个点到源点的最短路径。但如...

  • Dijkstra

  • Dijkstra

    1.问题 单源最短路径 2 最短路径数组就是当前各个节点到A的距离前驱数组就是相应距离的前一个节点 此时在V-S中...

网友评论

      本文标题:——Dijkstra

      本文链接:https://www.haomeiwen.com/subject/ijzdfftx.html