图论4-最短路(I)

三种最短路算法

Floyd

可求出任意两点间最短路,适合小规模稠密图(点数不超过 500 500 500)。

复杂度 O ( n 3 ) O(n^3) O(n3)

注意:如果 n n n超过 800 800 800,且图比较稀疏,可使用 n n n次dijkstra代替Floyd。

for(int k=1;k<=n;++k)
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);

堆优化dijkstra

可求出单源最短路,适合大规模稀疏图。

复杂度 O ( m log ⁡ n ) O(m\log n) O(mlogn)

不能处理带负权的图!!!!!!

typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > pq;
void dijkstra(int S){
    memset(dis,63,sizeof(dis));
    memset(vis,0,sizeof(vis));
    pq.push(pii(dis[S]=0,S));
    while(!pq.empty()){
        int u=pq.top().second;
        pq.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=fir[u];i;i=nxt[i]){
            int v=to[i];
            if(dis[v]>dis[u]+w[i])
                pq.push(pii(dis[v]=dis[u]+w[i],v));
        }
    }
}

SPFA

可求出单源最短路,且可以处理负权。适合带负权的大规模稀疏图。

复杂度:下界 O ( m ) O(m) O(m),上界 O ( n m ) O(nm) O(nm)

(关于如何卡到上界,见NOI2018 D1T1)

关于SPFA,它死了

在边权均为 01 01 01的图中复杂度是 O ( m ) O(m) O(m)

queue<int> q;
void SPFA(int S){
    memset(dis,63,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[S]=0,vis[S]=1;
    q.push(S);
    while(!q.empty()){
        int u=q.front();
        q.pop(),vis[u]=0;
        for(int i=fir[u];i;i=nxt[i]){
            int v=to[i];
            if(dis[v]>dis[u]+w[i]){
                dis[v]=dis[u]+w[i];
                if(!vis[v])vis[v]=1,q.push(v);
            }
        }
    }
}

Part 2.4.1:最短路

例题2.4.1:[FAIOJ2344]寻宝之路

Floyd求出任意两点最短路后,再对 d i s [ A i ] [ A i + 1 ] dis[A_i][A_{i+1}] dis[Ai][Ai+1]求和。

复杂度 O ( n 3 ) O(n^3) O(n3)

例题2.4.2:[FAIOJ30075]农场派对

分别在原图和反图上求出 x x x点到其他点的最短路,再统计最大值。

复杂度 O ( m log ⁡ n ) O(m\log n) O(mlogn)

例题2.4.3:[FAIOJ30078]新年好

对于 1 , a , b , c , d , e 1,a,b,c,d,e 1,a,b,c,d,e五个点分别求出到其他点单源最短路,然后全排列枚举所有 5 ! 5! 5!种方案统计最小值。

复杂度 O ( m log ⁡ n + 5 ! ) O(m\log n+5!) O(mlogn+5!)

例题2.4.4:[FAIOJ30074]架设电话线

最小化第 k + 1 k+1 k+1

类似于最小化最大值问题,二分答案 x x x

将边权 ≤ x \leq x x的边全部视为 0 0 0,边权 > x >x >x的边全部视为 1 1 1

然后求最短路,如果 1 1 1 n n n的最短路 ≤ k \leq k k就说明 x x x可行,应下调;否则上调。

复杂度 O ( m log ⁡ n log ⁡ W ) O(m\log n\log W) O(mlognlogW)

例题2.4.5:[ZJOI2006][FAIOJ20601]物流运输

本题看似很复杂,但数据范围极小,所以可以考虑暴力一些的做法,比如:求出每一段时间的最短路。

f [ i ] = f[i]= f[i]= i i i天的最优解,则有转移

f [ i ] = min ⁡ { f [ j − 1 ] + ( i − j + 1 ) ∗ L ( j , i ) + K } , 1 ≤ j ≤ i f[i]=\min\{f[j-1]+(i-j+1)*L(j,i)+K\},1\leq j\leq i f[i]=min{f[j1]+(ij+1)L(j,i)+K},1ji

其中 L ( j , i ) L(j,i) L(j,i)表示从第 j j j天到第 i i i天,不改变航线,从 1 1 1 n n n的最短路。用dijkstra等算法求解均可。

复杂度 O ( n 2 E log ⁡ V ) O(n^2E\log V) O(n2ElogV)

Part 2.4.2:分层图最短路

分层图也是图论中一种常见的“拆点”方法。我们通过一些例子具体理解。

例题2.4.6:[FAIOJ1650]飞行路线

显然,如果 k = 0 k=0 k=0,那么本题就是一道经典的最短路模型。

但本题允许我们将 k k k条边长视为 0 0 0

首先指出一种比较显然的贪心是错误的:把最短路中最长的 k k k条边视为 0 0 0

很简单的例子:

3 3 1
0 2
0 1 1
1 2 1
0 2 3

很明显答案是0。而上面的贪心会得到1

考虑到其实每个点都有 k + 1 k+1 k+1种状态:用了 0 , 1 , … , k 0,1,\dots,k 0,1,,k次免费票。

因此将每个点 u u u拆成 k + 1 k+1 k+1个点 u 0 , u 1 , … , u k u_0,u_1,\dots,u_k u0,u1,,uk

然后对于每条原图中的边 ( u , v , w ) (u,v,w) (u,v,w),对于每个 k k k,连边 ( u k , v k , w ) (u_k,v_k,w) (uk,vk,w),表示不用免费票;连边 ( u k , v k + 1 , 0 ) (u_k,v_{k+1},0) (uk,vk+1,0),表示用免费票。

然后做以 S 0 S_0 S0为起点的单源最短路即可。

复杂度 O ( k m log ⁡ n ) O(km\log n) O(kmlogn)

例题2.4.7:[NOIP2009][FAIOJ10092]最优贸易

本题有多种方法:贪心,搜索等。但本题也可以用分层图最短路做。

每个点都可以看做有三种状态:没交易过;已经买完但没卖;已经卖完。

因此建立分层图:

原图中的边权均为 0 0 0

第一层的每个点向第二层相同的点连边,边权为卖出价格;

第二层的每个点向第三层相同的点连边,边权为买入价格的相反数。

然后求出第一层的 1 1 1号点到第三层的 n n n号点的最短路即可。

复杂度 O ( m log ⁡ n ) O(m\log n) O(mlogn)

例题2.4.8:[FAIOJ330]汽车加油行驶问题

本题条件很多,但实际上只需分类讨论出向每个方向行驶所需费用即可。

首先,车在每个点的状态可以看做有 k + 1 k+1 k+1种:剩 0 , 1 , 2 , … , k 0,1,2,\dots,k 0,1,2,,k格油。因此建立 k + 1 k+1 k+1层分层图。每层图都是一张网格图。

对于同一层之间的点,因为汽车在任何情况下行驶都会耗油,所以同一层之间的点不连边;

i i i与自己相邻的 4 4 4个点都向第 i i i层的点连边表示耗一格油。

i i i与自己相邻的 4 4 4个点都向第 k k k层的点连边表示加满油后再走一格(因为加油是强制的)。

对于非加油站的点,向上一层

向上和向左连边边权为 0 0 0(没有费用)

向下和向右连边边权为 B B B(过路费)

向第 k k k

向上和向左连边边权为 A + C A+C A+C(增设加油站,同时加满油)

向下和向右连边边权为 A + B + C A+B+C A+B+C(过路费+油费)

对于加油站的点,向第 k k k

向上和向左连边边权为 C C C

向下和向右连边边权为 B + C B+C B+C

然后求 ( 1 , 1 , k ) (1,1,k) (1,1,k) ( n , n , i ) , 0 ≤ i ≤ k (n,n,i),0\leq i\leq k (n,n,i),0ik的最短路即可。

复杂度为 O ( k n 2 log ⁡ n ) O(kn^2\log n) O(kn2logn)

Part 2.4.3:最短路图

定义:所有可能在最短路,即满足 d i s [ u ] + w ( u , v ) = d i s [ v ] dis[u]+w(u,v)=dis[v] dis[u]+w(u,v)=dis[v]的边构成的图。

最短路图是一张DAG(有向无环图)。

因为DAG上的状态满足DP“无后效性”的要求,所以在DAG上是可以DP的。DP过程和树形DP类似,DP的顺序是图的拓扑序

queue<int> q;
for(int i=1;i<=n;++i)if(!d[i])q.push(i);
while(!q.empty()){
    int u=q.front();
    q.pop();
    for(int i=fir[u];i;i=nxt[i]){
        int v=to[i];
        if(!--d[v])q.push(v);
        //DP转移
    }
}

例题2.4.9:[FAIOJ30077]最短路计数

本题是最短路图上DP的模板题。

建立最短路图后,对于每条边,如果有 d i s [ u ] + W ( u , v ) = d i s [ v ] dis[u]+W(u,v)=dis[v] dis[u]+W(u,v)=dis[v],则进行转移 f [ v ] + = f [ u ] f[v]+=f[u] f[v]+=f[u]。否则不转移。

例题2.4.10:[SDOI2009][luogu2149]Elaxia的路线

先预处理出 S 1 , T 1 , S 2 , T 2 S_1,T_1,S_2,T_2 S1,T1,S2,T2到其他顶点的最短路。

对于一个点 x x x,如果有 d i s ( S 1 , x ) + d i s ( x , T 1 ) = d i s ( S 1 , T 1 ) , d i s ( S 2 , x ) + d i s ( x , T 2 ) = d i s ( S 2 , T 2 ) dis(S_1,x)+dis(x,T_1)=dis(S_1,T_1),dis(S_2,x)+dis(x,T_2)=dis(S_2,T_2) dis(S1,x)+dis(x,T1)=dis(S1,T1),dis(S2,x)+dis(x,T2)=dis(S2,T2),则说明 x x x在公共最短路上。

如果 x , y x,y x,y都在公共最短路上,那么 x , y x,y x,y在任一条最短路的路径也就都在公共最短路上。

因此暴力枚举 x , y x,y x,y并进行上述判断,然后对 ∣ d i s ( S 1 , x ) − d i s ( S 1 , y ) ∣ |dis(S_1,x)-dis(S_1,y)| dis(S1,x)dis(S1,y)取最大值即可。

复杂度 O ( n 2 + m log ⁡ n ) O(n^2+m\log n) O(n2+mlogn)

此算法可通过本题,然而它看起来是错的。。。

最短路图是一张DAG而非只有一条链,所以 x , y x,y x,y可能会在不同的链上,然后上面的 d i s dis dis之差就错了。

但由于这个错误的 d i s dis dis之差不会影响答案(最长公共路径一定会枚举到,而且错误的答案不会大于正确的答案),所以也可以通过。

但是这个 n 2 n^2 n2很麻烦(这道题其他的部分都是 O ( m log ⁡ n ) O(m\log n) O(mlogn),其实是可以加强出到 n , m ≤ 1 0 5 n,m\leq 10^5 n,m105的)。

所以正解仍然是DAG上的DP。设 f [ u ] = f[u]= f[u]=终点为 u u u的最长公共路径。

在第一张DAG图上DP,如果 ( u , v ) (u,v) (u,v)在第二张DAG图上则更新 f [ v ] f[v] f[v]的值。

最后取所有 f [ ] f[] f[]的最大值即可。

复杂度 O ( m log ⁡ n ) O(m\log n) O(mlogn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值