模拟赛2020.4.23C题题解

该博客解析了一道模拟赛中的图论问题,讨论如何通过最多反转一次边来最小化起点S到终点T的最短路径。博主首先介绍了问题背景,然后通过实例解释了反转边对最短路径的影响,并提出了利用Dijkstra算法分别从S和T构建最短路径图的方法。在枚举所有可能的边反转后,比较新路径是否更优,从而找出最佳策略。最后,博主强调了对于这类问题,通过手动绘制和推导图是理解规律的关键。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给你一个有向图,由n个点,m条边组成。你有一种魔法,可以将某条边的方向反转。在你最多使出一次魔法的情况下,使得起点S到终点T的距离最短。

思路要点

套路题。

首先我们把将一条边反转的图像画出来。

我们先考虑上图,没进行反转情况下的图。起点 S = 1 S=1 S=1,终点 T = 5 T=5 T=5,那么我们先跑一次 d i j k s t r a dijkstra dijkstra,得出最短距离为 min ⁡ ( p a t h ( 1 → 2 → 4 → 3 → 5 ) , p a t h ( 1 → 3 ) ) = p a t h ( 1 → 3 ) = 5 + 4 = 9 \min(path(1\rightarrow2\rightarrow4\rightarrow3\rightarrow5),path(1\rightarrow3))=path(1\rightarrow3)=5+4=9 min(path(12435),path(13))=path(13)=5+4=9

然后当反转 ( 5 , 2 ) (5,2) (5,2)这条边后,就变成了下图:

这时候我们发现最短路更新了, p a t h ( 1 → 2 → 5 ) = 3 + 1 = 4 path(1\rightarrow2\rightarrow5)=3+1=4 path(125)=3+1=4

这样已经出现了规律,我们再来考虑一幅更清晰的图。

这幅图中,起点 S = 3 S=3 S=3,终点 T = 6 T=6 T=6,那么最短路径长度为 p a t h ( 3 → 1 → 2 → 4 → 5 → 6 ) = 5 path(3\rightarrow1\rightarrow2\rightarrow4\rightarrow5\rightarrow6)=5 path(312456)=5。我们先保存这个最短距离 a n s = 5 ans=5 ans=5。接下来当我们反转 ( 1 , 5 ) (1,5) (1,5)这条边的方向是,最短路径长度就变成 p a t h ( 3 → 1 → 5 → 6 ) = 3 path(3\rightarrow1\rightarrow5\rightarrow6)=3 path(3156)=3

观察两幅图可以得出,当我们翻转 ( u , v ) (u,v) (u,v)这条边时,最短路 S → T S\rightarrow T ST会有一种新的走法 S → v → u → T S\rightarrow v \rightarrow u \rightarrow T SvuT。所以当我们反转边时,只要考虑点 S S S和点 T T T距离 ( u , v ) (u,v) (u,v)的距离。但由于有很多条边,很多的 ( u , v ) (u,v) (u,v),所以我们不能以 u u u v v v跑最短路,我们将 S S S正向建图跑最短路,设为 d i s S dis_S disS数组,将 T T T反向建图跑最短路,设为 d i s T dis_T disT数组,所以我们只要枚举每条边 ( u , v ) (u,v) (u,v),判断反转当前边后最短路是否更优,比较 d i s S [ v ] + w + d i s T [ u ] dis_S[v]+w+dis_T[u] disS[v]+w+disT[u] a n s ans ans即可,其中 w w w是边 ( u , v ) (u,v) (u,v)的权值。

算法流程

首先 O ( n l o g n ) O(nlogn) O(nlogn) S S S为起点的最短路,然后再 O ( n l o g n ) O(nlogn) O(nlogn) T T T为起点的反向的最短路。得出 d i s S dis_S disS d i s T dis_T disT数组后先保存不反转时的答案。然后枚举每一条边 ( u , v ) (u,v) (u,v)判断反转这条边后最短路是否更优即可。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,inf=0x3f3f3f3f;
struct node{
    int id,dis;
    bool operator < (const node & b)const
	{
	    return dis>b.dis;
    }
};
int n,m,S,T,x,y,cnt,ans,a[N],b[N],c[N],new_a[N],new_b[N],f[N],f1[N];
priority_queue<node> Q;//堆优化dijkstra
inline void add(int x,int y){a[++cnt]=y,b[cnt]=c[x],c[x]=cnt;}//邻接表建图
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>x>>y,add(x,y),new_a[i]=y,new_b[i]=x;
	cin>>S>>T;
	memset(f,inf,sizeof(f));f[S]=0;
	Q.push((node){S,0});
	while(!Q.empty()){
	    node x=Q.top();Q.pop();
		for(int i=c[x.id];i;i=b[i])
		    if(f[x.id]+1<f[a[i]])
			    f[a[i]]=f[x.id]+1,Q.push((node){a[i],f[a[i]]});
	}
    //以S为起点跑最短路
	cnt=0;memset(c,0,sizeof(c));
	for(int i=1;i<=m;i++)add(new_a[i],new_b[i]);
	memset(f1,inf,sizeof(f1));f1[T]=0;
	Q.push((node){T,0});
	while(!Q.empty()){
	    node x=Q.top();Q.pop();
		for(int i=c[x.id];i;i=b[i])
		    if(f1[x.id]+1<f1[a[i]])
		        f1[a[i]]=f1[x.id]+1,Q.push((node){a[i],f1[a[i]]});
	}
    //以T为起点跑反向的最短路
	ans=f[T];//不反转时的答案
	for(int i=1;i<=m;i++)ans=min(ans,f[new_a[i]]+1+f1[new_b[i]]);//枚举每一条边,判断答案是否更优
	return cout<<ans<<endl,0;
}

做题总结

对于一些反转边的方向的题目,有些是这种套路——跑正反两次最短路。这种题目其实并不难想,画几个图手推一下就可以找到规律了。有些难的最短路题一般是建图优化和跑最短路优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值