爆刷PAT(甲级)——之【1003】 Emergency (25)——最短路简单变形

题意:   N个城市M条边,边是无向变。每个边有权值,以及每个城市都有某个数量的人。

       给出起点城市和终点城市,要求从起点到终点的最短距离条件下的—— 路径数,以及最多能捎上多少人!(路径上城市的人数和最大)

难点:

      数据量N小于500。作为PAT第三题,是最短路的路径数问题以及小变形。Dijkstra、Bellman什么的基本操作都学过,但是一是忘了大半,二是没有吃透。所以今天做这个简单的小变形,1个半小时还没有做出来。较难以理解如何    “在松弛点上如何变形”  。 

      参照了别人的AC代码,果不其然是在松弛点上操作。要分别将 “路径数”的维护操作  以及  “路径经过的最大人数”  维护操作,在松弛操作时候一同联系进去,说实话,虽然知道代码怎么写,但还是没有理解为什么可以这样。。。。

      这种小变形应该是算很简单的小变形, 以至于各位江湖朋友写博客都不写为什么这么写,往往只是写 “Dijkstra的小变形” 。。。。emmmm   ,睡了一觉,第二天清醒 的脑袋,一下就理解了为什么可以这样写。。。虽然我还是解释不清啊。。。

 

1、本题我使用的是Dijkstra算法

2、每一轮找到最近未标记过点u之后,按照Dijkstra算法,接下来是用点u去尝试松弛剩下的所有点。在这里:

  如果通过点u,无法松弛某点 i ,但是路径大小相同,那么这可能就是一条新的最短路径,那么到达 i 点的方法数 = 自己原本的值 + 到u点方法数

  如果通过点u,无法松弛某点 i ,但是路径大小相同,说明当前路径是最短路径的一条!所以还要考虑,对“最短路径”的最大人数进行维护。 如果  (到u点时能捎上的最大人数  +  i点本身城市驻扎人数)  > (当前记录的 i 点能捎上的最大人数) ,就更新这个值

  如果 可以通过点u,松弛某点 i 时,那么此点的最短路径必然更新,如果最短路径都更新了,那么  最大人数  肯定也要 “覆盖”

3、 我写的Dijkstra模板比较懒,写完以后,跑出来的“最大人数”的结果一直都比样例大。百思不得其解。观摩了别人的AC代码之后,才最后终于明白是因为我的代码里,松弛的循环部分少了 一句 (if(!book(i))) ,加上就AC了。但是,为什么呢?

想了一会明白——我精简这个模板的时候,可以删去了这句话,是因为,只讨论最短路的Dijkstra时候,每一次被选中的松弛点,必然是没有被标记过的剩下的点中,和起点距离最近的点!这是Dijkstra找 松弛点的算法本质。  而下一步的松弛循环中,用松弛点对松弛点本身进行松弛,是不会影响结果的

但是,对最短路进行变形的时候,因为引入了一个 “最大人数” 的维护与讨论在松弛阶段,用松弛点对松弛点进行松弛的话,会导致 “误判” 的,对 “最大人数” 的重新累加。就会导致结果翻倍

也就是说,如果我把代码的(if(!book(i))) 改成 (if( i!=u  )) 也是可以的!因为他们的目的都已经达到了,就是不对松弛点u重新松弛即可

 

终于AC了,学到了学到了。我对Dijkstra还是不够了解呐。

Code:

我的这个代码中,用的就是if( i!=u  )来防止松弛点重复松弛了,给自己一个警戒。

#include
#include
#include
#include
#include
#include
using namespace std;
#define inf 509
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;xdis[u]+path[u][i])
            {
                dis[i]=dis[u]+path[u][i];//缩短边
                num[i]=num[u];

                //如果当前的路径是最短路,那么路径上的救援人数当然应重新赋值
                //因为以前的救援人数已经不是“最短的路径”上的了
                //救援队最大队伍,等于能到达U点的人数,加上I点城市自己的人数
                rescue[i]=rescue[u]+men[i];
            }
            else if(dis[i]==dis[u]+path[u][i])
            {
                //如果通过u点到达i点的距离相同,
                //那么到达u点的方法数就会累加到到达i点的方法数上
                num[i]+=num[u];

                //如果当前路径也是最小的,那么就要判断(维护)当前的最大救援人数值了
                rescue[i]=max(rescue[i],rescue[u]+men[i]);
            }
        }
    }
}

void Output()
{
    printf("%d %d\n",num[g],rescue[g]);
}

int main()
{
    Input();
    Dijkstra();
    Output();
    return 0;
}

 

你可能感兴趣的:(最短路,PAT甲级【爆刷】)