最简单的次短路算法

我的第一篇博客

2020-01-01

第一种方法

步骤
  1. 起点s,终点t;
  2. 找s到每个点的最短路并用dis1数组记下来;
  3. 找t到每个点的最短路并用dis2数组记下来;
  4. 访问每条边,用u表示这条边的起点,v表示这条边的终点,w表示权值,用k记下s到u的最短路加上v到t的最短路再加上w;
  5. 比较k和dis1[ t ]的大小,如果k > dis1[ t ],就与ans比较,如果小于ans,则更新ans的值为k;
  6. 次短路就为ans;

代码如下(1到n的次短路):

#include
#include
#include
#define maxn 5005
#define maxm 200005
#define inf 0x7fffffff
#define ri register int 
using namespace std;
priority_queue<pair<int,int> >q;
int from[maxm];//边的起点 
int to[maxm];//边的终点 
int weight[maxm];//边的权值 
int next[maxm];//下一条边的编号 
int head[maxn];//上一条边的编号 
int vis[maxn];//判断该点是否已经找到最小值 
int dis[maxn]; 
int dis1[maxn];//1到每个点的最短路 
int dis2[maxn];//n到每个点的最短路 
int n,m,t,cnt,k,ans=inf;
inline int read()//快读 
{
	ri x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline void add_edge(int x,int y,int z)//建边 
{
	cnt++;
	from[cnt]=x;
	to[cnt]=y;
	weight[cnt]=z;
	next[cnt]=head[x];
	head[x]=cnt;
}
inline void Dijkstra(int s,int t)
{
	memset(vis,0,sizeof(vis));
	for(ri i=0;i<=n;++i)
		dis[i]=inf;
	dis[s]=0;
	q.push(make_pair(-dis[s],s));
	do
	{
		k=q.top().second;
		q.pop();
		if(vis[k]) continue;
		vis[k]=1;
		for(ri i=head[k];i;i=next[i])//遍历每条边 
		{
			ri v=to[i];
			ri w=dis[k]+weight[i];
			if(w<dis[v]) 
			{
				dis[v]=w;//更新最短路 
				q.push(make_pair(-dis[v],v));
				vis[v]=0;
			}
		}
	}while(q.size());//判断队列是否为空 
}
int main()
{
	n=read(),m=read();
	for(ri i=1,x,y,z;i<=m;++i)
	{
		x=read(),y=read(),z=read();
		add_edge(x,y,z);
		add_edge(y,x,z);
	}
	Dijkstra(1,n);
	for(ri i=1;i<=n;++i)
		dis1[i]=dis[i];
	Dijkstra(n,1);
	for(ri i=1;i<=n;++i)
		dis2[i]=dis[i];
	for(ri i=1;i<=m*2;++i)
	{
		ri u=from[i],v=to[i],w=weight[i];
		ri k=dis1[u]+dis2[v]+w;//1到u的最短路,加上v到n的最短路,再加上w 
		if(k>dis1[n]&&k<ans) ans=k;//更新次短路 
	}
	printf("%d",ans);	
	return 0;
}

第二种方法

  • 同时找最短路和次短路
#include
#include
#include
#define maxn 5005
#define maxm 200005
#define ri register int
#define inf 0x7fffffff
using namespace std;
priority_queue<pair<int,int> >q;//定义优先队列 
int to[maxm];//边的终点
int weight[maxm];//边的权值
int next[maxm];//下一条边的编号 
int head[maxn];//上一条边的编号
int dis1[maxn];//1到每个点的最短路 
int dis2[maxn];//1到每个点的次短路 
int n,m,cnt;
inline int read()//快读 
{
	ri f=1,x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
void add_edge(int u,int v,int w)//建边 
{
	to[++cnt]=v;
	weight[cnt]=w;
	next[cnt]=head[u];
	head[u]=cnt;
}
void dijkstra()
{
	dis1[1]=0;
	q.push(make_pair(-dis1[1],1));
	while(!q.empty())
	{
		ri k=q.top().second,ki=-q.top().first;//因为进去的取了相反数,所以出来要添负号 
		q.pop();
		if(dis2[k]<ki) continue;//如果该点的次短路已经比要更新的小了,那么就不需要更新 
		for(ri i=head[k];i;i=next[i])
		{
			ri v=to[i],s=ki+weight[i];
			if(s<dis1[v])
			{
				dis1[v]=s;//更新最短路 
				q.push(make_pair(-dis1[v],v));
			}
			if(s>dis1[v]&&s<dis2[v])//比最短路长并且比次短路短 
			{
				dis2[v]=s;//更新次短路 
				q.push(make_pair(-dis2[v],v));
			}
		}
	}
}
int main()
{
	n=read(),m=read();
	for(ri i=1,x,y,z;i<=m;++i)
	{
		x=read(),y=read(),z=read();
		add_edge(x,y,z);
		add_edge(y,x,z);
	}
	for(ri i=1;i<=n;++i) dis1[i]=dis2[i]=inf;//都赋最大值 
	dijkstra();
	printf("%d",dis2[n]);
	return 0;
}

两道练习题
洛谷P2865
洛谷P2829

你可能感兴趣的:(图论)