zoukankan      html  css  js  c++  java
  • 最短路径纯贪心算法。

     

        Dijkstra算法,书上其实说的很简洁,仔细看,仔细思考是会理解的.但要先理解几条引论和推理.

        而自己思考的思路在不需要任何推理只从贪心思路出发,和Dijkstra有所不同,但本质一样,那么自己按照理解,试着慢慢讲下.

    一,问题:

    从某个源点,到其他各点的最短路径.

    注意,不要想成某一个点,到某个点的最短路径.这样的话不太好推导出思路。

    某个源点,到其他各点的最短路径.这样的思路反而好推导出,反正所有点都计算出来了。某点到某点就是其中一条而已。,

    二,分析.

    先抛弃书中所有关于最短路径的引理,定理,公理....

    先看图.

      要找到从0点.到所有点的最短路径.

      假如,小明,站在0点.他很渴,天气很热,再不喝水要渴死,而每个其他点,都有一瓶水,点和点之间的交通工具还不一样,有的是汽车,有的还必须走路. 

      假设这里的权值代表两点之间所花费的时间,比如0 ~3中间的数字6, 表示.从0到3要6个小时的慢车.

      打开地图一看,到底哪个是最短啊.他想喝3点的水,发现要9个小时. 居然比 先到2,再到4, 7个小时还长,那么去4吧.你当小明傻啊.到了2点,不喝水,还去4点.

      所以小明得到一个常识,对于第一瓶水,需要中转点而到达的目的点肯定不是花费时间最少的,最起码可以喝那个中转站的水.

      直连的点话,看看哪个点花费的时间少.  1点最少,只要3个小时.

      废话少说,小明直接从0做车到1,花费3个小时,喝到了第一瓶水,刚喝完水,小明被时光机, 卟的一声,居然传回了0点.(因为我们要从0开始,找出到所有点的最短路径,所以小明被传送了.)

      因为小明还是很渴,天气依然很热,再不喝水要渴死.

      那么下一个最近的点在哪里?

      小明开始思索, 1点是和0直连最近的.已经被我喝掉了.那么舍去1点,第二近的直连点是不是最近的呢?

      先看看.发现了是6点.要4个小时.

      其他直连点先全部抛弃,因为这个时候6点是直连点最近的了.

      还有没有比4个小时更短的?有,是之前的1点,只要3个小时,但喝掉了.

      小明思索一会,有答案了,唯一可能的是,如果原来1点,离它很近的地方有顶点.那么有可能比6点近.

      看看地图,1可以到4和5.时间是5和8. 总时间就是 3+5和3+8.明显比4个小时长.

      小明马上又坐车从0点到了6点.喝光水,毫无疑问, 卟的一声小明被时光机,又传回了0点.

      不过有了上次的思考.

      小明有了初步总结.

      下一个最近的可能是2种方案中找一个

        1)和0直连的点,但要除去1,和6.也就是和0直连的 第3近的点.

        2)从最早的的1点出发,所能到达的点,虽然上次排除了,但说不定这次和第3近的点有的一拼.

      正要查地图的时候.小明突然想到. 6点是第二近的点.说不定从6点出发,也有比 直连第3近的点更短呢.

      终于,小明整理出了一个方案. 

      用一个数组MinRD[]  ,存放已知最短路径.

      用另外一个数组noGet[],存放 所有点 减去 所有最短路径的终点.

      每次从最短路径MinRD[]中,查询每条最短路径的终点. 再写下这些终点,到 未曾到达的点的权直.再找出最小的.

        这里要想明白, 假如有一条最短路径经过了好几个点,如 0->1->3->6->7. 小明最后一次喝掉了7点的水,

              也意味着,他之前喝掉了6点的水,也就是0->1->3->6是一个最短路径,    

              0->1->3->6->7是从0->1->3->6这个最短路径,由6顶点和其他最短路径的顶点通过残酷的比较中选出 来的.   

              这其实就是最优路径的的一个引论,最短路径,中间的路径也是最短路径.喝水喝出了引论.

      不厌其烦的,我要写下每次程序运行的过程.

    1)第一次找水.

    已知最短路径

    0                      这个是原点,默认放入.

    还未到达过的

    1,2,3,4,5,6

    只有一个终点0. 从终点出发,和还未到达过的点组成弧,找到最断的.

    比较 v(0,1),v(0,2),,v(0,3),v(0,4),v(0,5),v(0,6)

    0->1最短,加入到最短路径集合中.

    2)

    已知最短路径

    0

    0->1

    还未到达过的

    2,3,4,5,6

    第一个最短路径的终点是0.第二条最短路径的终点是1

    从终点0和1出发,和还未到达过的点组成弧,找到最断的.

    比较  v(0,2),,v(0,3),v(0,4),v(0,5),v(0,6),

            v(1,2),v(1,3),v(1,4),v(1,5),v(1,6)

    0->6 最短,加入到最短路径集合中

    3)

    已知最短路径

    0,                     

    0->1                第一次找到的.

    0->6                第二次找到的.

    还未到达过的

    2,3,4,5

    比较  v(0,2),,v(0,3),v(0,4),v(0,5)

            v(1,2),v(1,3),v(1,4),v(1,5)

       v(6,2),v(6,3),v(6,4),v(6,5)

    最优路径终点0,1,6,

    0->6->5 和 0->2 一样,

    根据程序判断顺序,会加入其中一个,下次查找会加入另外一个. 也可以修改程序,让他们一次加入

      c  代码如下(代码可读性一般)

    struct Minroads
    {
        int *road;
        int length;
        int cost;
    };
    
    
    int main()
    {
        //临街矩阵,直接写出,就不用程序生成了,数字表示2点间弧的权直,-1表示2点不能直达。
        int matrix[7][7]={
        0,3,5,9,-1,6,4,
        3,0,-1,-1,8,5,-1,
        5,-1,0,-1,2,-1,-1,
        9,-1,-1,0,-1,-1,-1,
        -1,8,2,-1,0,-1,-1,
        6,5,-1,-1,-1,0,1,
        4,-1,-1,-1,-1,1,0
        };
    
        struct Minroads MinRD[7];//初始化最短路径数组
        int i=0;
        for(i=0;i<7;i++)
        {
            MinRD[i].road=malloc(sizeof(int)*7);
            MinRD[i].road[0]=0;
            MinRD[i].cost=0;
            MinRD[i].length=0;
        }
    
        int noGet[7]={0,1,2,3,4,5,6};//初始化未曾到达的顶点集合。
    
    
        MinRD[0].road[0]=0;//把源点自己(起点)作为第一条最短路径加入最短路径集合(包含1个顶。权直和为0)
        MinRD[0].length=1;
        MinRD[0].cost=0;
    
        noGet[0]=-1;// 简单的把到达的点,标记为-1,0加入就把索引为0的数值改为-1。当nouse的元素全部为-1。那就全部到达。
        int get_count=1;//
    
    
    
        //从每条已知的最短路径出发,走一步看看。比较所有产生的新路径,看看谁最短。
        //程序描述就是找出 ,minrd中,每个road的权直,加上,这条road的顶点到noGet中所有顶点的权直中最小的。
        while(get_count<7)
        {
            int i_noget,i_shortest;
            int temp_short=0x7fffffff;//int 是有符号数,最高位为0,表示最大正数,
            int temp_s, tmep_d;
            for(i_shortest=0;i_shortest<7;i_shortest++)//所有最短路径
            {
                if(MinRD[i_shortest].length!=0)//
                {
                    for(i_noget=0;i_noget<7;i_noget++)//未曾到达的顶点
                    {
                        if(noGet[i_noget]!=-1)
                        {
                            int x;
                            x=MinRD[i_shortest].road[MinRD[i_shortest].length-1];//最短路径的顶点
                            int y=noGet[i_noget];
                            int newshort;
                            if(matrix[x][y]!=-1)
                            {
                                newshort=MinRD[i_shortest].cost+matrix[x][y];
                            }
                            else
                            {
                                newshort=0x7fffffff;
                            }
                            if(newshort<=temp_short)
                            {
                                temp_short=newshort;
                                temp_s=i_shortest;
                                tmep_d=i_noget;
                            }
                        }
                    }
                }
            }
    
    
            int i_copy;
            for(i_copy=0;i_copy<MinRD[temp_s].length;i_copy++)
            {
                MinRD[get_count].road[i_copy]=MinRD[temp_s].road[i_copy];
            }
            MinRD[get_count].road[i_copy]=tmep_d;
    
            MinRD[get_count].length=MinRD[temp_s].length+1;
            MinRD[get_count].cost+=temp_short;
    
            noGet[tmep_d]=-1;
    
            get_count++;
        }
    
        int i_print;
        for(i=0;i<7;i++)
        {
            for(i_print=0;i_print<MinRD[i].length;i_print++)
            {
                if(i_print!=MinRD[i].length-1)
                {
                    printf("%d->",MinRD[i].road[i_print]);
                }
                else
                {
                    printf("%d",MinRD[i].road[i_print]);
                }
    
            }
            printf("  cost:%d",MinRD[i].cost);
            printf("
    ");
        }
    }

    结果:

    再次注意这里

    0->6->5.这条最优路径找出来的话。上面必定有0->6这条最优路径。

    闭着眼睛想一下,0->6->5是最优的话,没有理由,0—>6不是。如果0->6不是,而是0->x.那么,0->x->5,肯定比0->6->5更短。

    Dijkstra(迪杰斯特拉),是每次加入一条最短路径,那么更新   每个未处理的点    到     最短路径的顶点集合  的最短路径直。 选出最小的那个直。

    注意当加入第一个原点的时候,本身的权直就相当于更新过一遍.

    因为更新过,

    能体现出动态调整的思路。

    而我们的思路

    体现的是贪心算法的思路。省掉了更新的步骤,每次重新计算所有的点.所以导致比Dijkstra运算量稍微大点。

    不过自己推导出来,并能理解的,总感觉更亲切。

    下篇看看怎么更容易理解的方式写出Dijkstra

    几年后翻回来看。其实自己的想出来的思路,完全正确。就是贪心思路。

    而Dijkstra是贪心+动态。当年用贪心想到就结束了。没有继续优化,保存中间值,否则就是Dijkstra算法了。

    夸奖一下自己很难吗?比心。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 矩阵加法
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 乘法表
    监管只是压倒网盘业务的一根稻草,但不是主要原因(答案只有一个:成本!)
  • 原文地址:https://www.cnblogs.com/lsfv/p/5532145.html
Copyright © 2011-2022 走看看