• 贪心 单源最短路径


     最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

         确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法

      确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。

      确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径

      全局最短路径问题 - 求图中所有的最短路径。Floyd-Warshall算法
     
    这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v 除 s 外d[v] = ∞)。当算法结束时,d[v] 中储存的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。 Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 u 的路径。这条路径的长度是 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) 进行拓展。
    http://baike.baidu.com/view/4719095.htm
     
    dijkstra算法思想:
    开始时,S={u},T=V-{u}; 对T中所有顶点x,如果u到x存在边,置d(u,x)=c(u,x); 否则d(u,x)=无穷大。然后,对T中所有顶点x,
    寻找d(u,x)最小的顶点t,即:
     d(u,t)=min(d(u,x)|x属于T)
    则d(u,t)就是顶点t到顶点u的最短路径距离。同时,顶点t也是集合T中所有顶点距离u最近的顶点。把顶点t从T中删除,把它并入S。然后对T中与t相邻接的所有顶点x,用下面的公式更新d(u,x)的值:
     d(u,x)=min(d(u,x), d(u,t) +c(t,x);
    继续上面的步骤,一直到T空为止。
    类似代码实现:
    #include<iostream>
    using std::iostream;  //不能用using namepace std;应为utility中有一个函数 prev,在传递参数prev,编译错误不明确
    using std::cout;
    using std::cin;
    using std::endl;
    
    const int maxnum = 100;
    const int maxint = 999999;
     
    //各数组都从下标1开始
    int dist[maxnum]; //表示当前点到原点的最短路径长度
    int prev[maxnum]; //记录当前点的前一个结点
    int c[maxnum][maxnum]; //记录图的两点间路径长度
    int n,line; //图的结点数和路径数
    // n -- n nodes
    // v -- the source node
    // dist[] -- the distance from the ith node to the source node
    // prev[] -- the previous node of the ith node
    // c[][] -- every two nodes' distance
                           
    void Dijstra(int n,int v,int *dist,int *prev,int c[maxnum][maxnum])
    {
        bool s[maxnum]; //是否应该存到S集合中
        for(int i=1;i<=n;i++)
        {
            dist[i]=c[v][i];
            s[i]=0; //初始都未用过该点
            if(dist[i]==maxint)
                prev[i]=0;
            else
                prev[i]=v;
        }
        dist[v]=0;
        s[v]=1;
        // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
        // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
        // 注意是从第二个节点开始,第一个为源点
        for(int i=2;i<=n;i++)
        {
            int tmp=maxint;
            int u=v; 
            //找出当前未使用的点J 的dist[j]的最小值
            for(int j=1;j<=n;j++)
                if(!s[j]&&dist[j]<tmp)
                {
                    u=j;   // u保存当前邻接点中距离最小的点的号码
                    tmp=dist[j];
                }
            s[u]=1; //表示点u已存入S集合中
            //跟新dist
            for(int j=1;j<=n;j++)
                if((!s[j])&&c[u][j]<maxint)
                {
                    int newdist=dist[u]+c[u][j];
                    if(newdist<dist[j])
                    {
                        dist[j]=newdist;
                        prev[j]=u;
                    }
                }
        }
    
    }
    
    
    //查找从原点v到终点u的路径,并输出
    void  searchPath(int *prev,int v,int u)
    {
        int que[maxnum];
        int tot=1;
        que[tot]=u;
        tot++;
        int tmp=prev[u];
        while(tmp!=v)
        {
            que[tot]=tmp;
            tot++;
            tmp=prev[tmp];
        }
        que[tot]=v;
        for(int i=tot;i>=1;--i)
            if(i!=1)
                cout<<que[i]<<"->";
            else
                cout<<que[i]<<endl;
    }
    
    
     
    
    
    int main()
    {
        freopen("dijkstra.txt","r",stdin);
         
    
        //各数组下标都从1开始
        //输入结点数,
        cin>>n;
        //输入路径数
        cin>>line; 
        int p,q,len; //输入p ,q两点及其路径长度
        //初始化c[][]为maxint
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                c[i][j]=maxint;
        for(int i=1;i<=line;++i)
        {
            cin>>p>>q>>len;
            if(len<c[p][q]) //有重边
            {
                c[p][q]=len; //p指向q
                c[q][p]=len; //q指向p,这样表示无向图
            }
        }
        for(int i=1;i<=n;++i)
            dist[i]=maxint;
        /*
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;j++)
                printf("%8d",c[i][j]);
            printf("
    ");
        }
        */
        Dijstra(n,1,dist,prev,c);
    
        //最短路径长度
        /*
        cout<<"源点到最后一个顶点的最短路径长度: "<<dist[n]<<endl;
        //路径
        cout<<"源点到最后一个顶点的路径为: "<<endl;
        */
        cout<<"顶点a到个顶点的最短路径为"<<endl;
        for(int i=2;i<=n;i++)
        {
            searchPath(prev,1,i);
        }
        /*
        searchPath(prev,1,2);
        searchPath(prev,1,3);
        searchPath(prev,1,n);
        */
    }
       

    参考:http://www.wutianqi.com/?p=1890

  • 相关阅读:
    关于C#调用VC SDK小结
    失业日志:2009年10月15日 猫 老鼠 人(一)
    闲聊日志 一:
    按编号分类的树
    不知不觉又用到了VC6
    这个问题很郁闷.net mvc,也可能我不知道
    失业日志:2008年10月13日
    失业日志:2009年10月16日 能回忆多少就写多少
    失业日志:2009年10月12日星期一
    失业日志 200910月22日
  • 原文地址:https://www.cnblogs.com/youxin/p/3263628.html
Copyright © 2020-2023  润新知