它只能反向一条边,所以我们不妨枚举一下这是哪一条边.
我们考虑反向这条边可能形成的三种情况(其实是四种):
1、(1 ightarrow n) 走最短路,(n ightarrow v_i ightarrow u_i ightarrow 1), 即必经 (v_i ightarrow u_i) 的最短路.
2、(n ightarrow 1) 走最短路,(1 ightarrow v_i ightarrow u_i ightarrow n), 即必经 (v_i ightarrow u_i) 的最短路.
3、(1 ightarrow v_i ightarrow u_i ightarrow n),(n ightarrow v_i ightarrow u_i ightarrow 1),即两种都走必经 (v_i ightarrow u_i).
注意前三种都要加 (d_i).
4、(1 ightarrow n) 和 (n ightarrow 1) 都走最短路(这种提前判断就好了).
那怎么求出必经某条边的最短路呢?其实很简单,记录一下 (1) 到所有点,(n) 到所有点,所有点到 (1),所有点到 (n) 的最短路,跑四遍dij,后两个在反图上跑(当然你也可以floyd)。
设 (dis(i,j)) 为 (i ightarrow j) 的最短路,则必经第一种 (v_i ightarrow u_i) 的最短路即为 (dis(n,v_i)+len(v_i,u_i)+d(u_i,1)).
可是这样子是错的,当然对于子任务2这样子是对的。
我们来思考为什么是错的,为什么子任务2又是对的呢?
原因是我们反向了某条边后如果这条边在原先最短的必经路径上的话原先的最短路就不存在了,而子任务2每条边都有一条与之相同的重边就不会影响最短路。
我们考虑必经边有多少条,我们可以处理出最短路径树,而必经边一定在树上,树边最多有 (n) 条,那么如果是树边就暴力重跑一遍暴力就好了(就是子任务1的暴力)。
这题代码实现其实还有点小难,这里就讲一下实现细节吧。
首先我们先把原图建出来,跑一遍 (1 ightarrow n) 和 (n ightarrow 1) 的最短路,并把最短路树建出来。这里就记录一下节点 (x) 是从哪个节点扩展来的就行了,然后把所有树边打上标记。计算出我上面讲的四个数组,当然后两个建反图即可。枚举每一条边,如果这条边是树边就把原来的边删掉从新建图,建的时候就这一条边反向,其它正常,然后跑最短路。否则也按我上面所说的三种情况扩展。
这样做貌似常数挺小的,直接跑到loj最优解第一页。评测记录及代码.
这份代码有点小问题,注意初始化的时候你的 (inf imes 4) 不能炸longlong否则判 (-1) 时就有问题。
可是这样不是最优的,因为树边不一定是必经边,所以我们每个必经边才暴力,看一眼学长写的代码.
还有个学长用了不知道什么的办法(可能是底层优化)跑到了最优解第2。我一看第一页有4个是我们机房的/jk.
写了挺久的给个赞吧。