算法练习题34---蓝桥杯2021省赛“路径”


前言

蓝桥杯2021年,填空题(C++)

题目考察了利用dijkstra算法去求最短路径的问题

一、题目描述

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。

小蓝的图由 2021 个结点组成,依次编号 1 至 2021。

对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。

例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。

请计算,结点 1 和结点 2021 之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

二、思路

从题目可以看出,需要求出最短路径,那么在最短路径问题中平时经常会用到迪杰斯塔拉算法。迪杰斯塔拉算法的主要思想在于一个公式:if(dis[next]>last.n_dis+next.w)dis[next]=last.n_dis+next.w

其中dis[next]表示邻居结点next到初始点的距离,而last.n_dis则表示当前邻居结点之前的那个结点(由该结点才引向了当前邻居结点)到初始点的距离,后者是当前邻居结点之前的那个结点的距离,最终选择一个更小的值来作为路径。

这么说可能比较绕,核心的思想用一句话来概括就是对于每一个要遍历的结点,都会由从该结点寻找所有可能的邻居结点,并计算好这些邻居节点们的最短路径,通过前述公式,然后将这些邻居节点们再装入队列,重复以上操作。

最终答案:10266837

三、具体代码

#include<bits/stdc++.h>
using namespace std;
const long long INF=1e12;
const long long NUM=2030;
struct edge  //记录边之间的信息
{
	long long start,to,w;  //start是该边的起始点,而to是该边的终点,w是距离
	edge(long long a,long long b,long long c)
	{
		start=a;
		to=b;
		w=c;
	}
};
vector<edge> e[NUM];   //用于存储图,这是一个二维数组
struct s_node	//记录一个结点的相关信息
{
	long long id;		//结点编号
	long long n_dis;		//n_dis,这个结点到起点的距离
	s_node(long long b,long long c)
	{
		id=b;
		n_dis=c;
	}
	bool operator<(const s_node&a)const  //重载运算符
	{
		return n_dis>a.n_dis;  //这一步的原因在于priority优先队列默认从大到小排序,如果想要从小到大排序,则需要对运算符重置
	}
};
long long n; //总共有n个结点
long long dis[NUM];  //记录所有结点到起点的距离
void dijkstra()
{
	long long s=1;  //起点是1
	bool done[NUM];  //done[i]=true表示到结点i的最短路径已经找到
	for(long long i=1;i<=n;i++)		//初始化
	{
		dis[i]=INF;
		done[i]=false;
	}
	dis[1]=0;	//起点到自己的距离是0
	priority_queue<s_node>q;	//优先队列,存结点信息
	q.push(s_node(s,0));	//起点进队列
	while(!q.empty())
	{
		s_node temp=q.top();
		q.pop();
		if(done[temp.id])  //表示当前结点的最短路径已经被处理好了
		{
			continue;
		}
		done[temp.id]=true;
		for(long long i=0;i<e[temp.id].size();i++)  //分别遍历e的所有邻接点
		{
			edge next=e[temp.id][i];  //表示第i个结点
			if(done[next.to])  //如果当前结点已经被处理好了
			{
				continue;
			}
			if(dis[next.to]>next.w+temp.n_dis)  //算法核心,选择更短的路径
			{
				dis[next.to]=next.w+temp.n_dis;
			}
			q.push(s_node(next.to,dis[next.to]));  //邻居结点入队列
		}
	}
}
long long gcd(long long a,long long b)
{
	if(a<b)
	{
		long long temp=a;
		a=b;
		b=temp;
	}
	if(b==0)
	{
		return a;
	}
	else
	{
		return gcd(b,a%b);
	}
}
int main()
{
	n=2021;
	for(long long i=1;i<=n;i++)
	{
		for(long long j=i;j<=n;j++)
		{
			if(abs(i-j)<=21&&i<j)  //避免重复
			{
				long long w=i*j/gcd(i,j);  //两点之间的距离
				e[i].push_back(edge(i,j,w));
				e[j].push_back(edge(j,i,w));
			}
		}
	}
	dijkstra();
	cout<<dis[2021]<<endl;	
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨大熊的代码世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值