思路:用迪杰斯特拉算法跑一遍,这题的意义在于重温图论里边的一些算法和最小公约数(gcd)最大公倍数的求法(lcm)
这里图采用链式前向星存储边,可以参考这篇博客:
而关于迪杰斯特拉算法,可以参考一下这篇博客:
迪杰斯特拉(Dijkstra)算法最通俗易懂的讲解_goodxin_ie的博客-CSDN博客_迪杰斯特拉算法
我的理解就是维护一个距离数组和一个集合s,每次从在s外的点中选出从到s最短的一个点,加入集合,然后更新其他未在集合中的邻接点的最小路径值
代码如下所示:
#include <iostream>
#include <queue>
#include <memory.h>
using namespace std;
typedef long long ll;
const int Max = 1e6 + 5;
int u[Max], v[Max], w[Max];
int g = 0, first[Max], nex[Max];
struct node
{
int n, w;
bool operator<(const node& a)const
{
return w > a.w;
}
node(int a, int b)
{
n = a;
w = b;
}
};
ll dis[Max], b[Max];
priority_queue<node> que;
//链式前向星存储
void add(int a, int b, int c)
{
u[++g] = a;
v[g] = b;
w[g] = c;
nex[g] = first[a];//记录上一条以a为起点的边在uvw中的存储位置
first[a] = g;//以a为起点的最后输入的边的存储位置
}
ll gcd(ll a, ll b)
{
if (a == 0) return b;
return gcd(b % a, a);
}
ll lcm(ll a, ll b)
{
return a * b / gcd(a, b);
}
int main()
{
// 请在此输入您的代码
memset(first, -1, sizeof(first));
memset(dis, 0x7f, sizeof(dis));
for (int i = 1; i <= 2021; ++i)
{
for (int j = i + 1; j - i <= 21 && j <= 2021; ++j)
{
add(i, j, lcm(i, j));
add(j, i, lcm(i, j));
}
}
que.push(node(1, 0));
dis[1] = 0;
while (!que.empty())
{
node no = que.top();
que.pop();
if (b[no.n]) continue;//已经加入集合
dis[no.n] = no.w;
b[no.n] = 1;//加入集合
//对这个节点的所有不在集合中的邻接点 更新最小值
for (int i = first[no.n]; i != -1; i = nex[i])
{
if (b[v[i]]) continue;
if (dis[u[i]] + w[i] < dis[v[i]])
{
dis[v[i]] = dis[u[i]] + w[i];
que.push(node(v[i], dis[u[i]] + w[i]));
}
}
}
cout << dis[2021];
return 0;
}