小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
【朴素dijkstra算法】
#include<bits/stdc++.h>
using namespace std;
const int N=3000;
const int INF=0x3f3f3f3f;
int g[N][N],dist[N];
bool st[N];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<2020;i++)
{
int t=-1;
for(int j=1;j<=2021;j++)
{
if(!st[j]&&(t==-1||dist[j]<dist[t]))
t=j;
}
for(int j=1;j<=2021;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
st[t]=true;
}
}
int main()
{
for(int i=1;i<=2021;i++)
{
for(int j=i;j<=2021;j++)
{
if(i==j) g[i][j]=0;
else if(j-i>21) g[i][j]=g[j][i]=INF;
else
{
int t=gcd(i,j);
int tt=i*j/t;
g[i][j]=g[j][i]=tt;
}
}
}
dijkstra();
cout<<dist[2021];
return 0;
}
【spfa算法】
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+10;
int h[N],ne[N],dist[N],e[N];
int w[N],idx;
bool st[N];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void spfa()
{
memset(dist,0x3f,sizeof dist);
queue<int> q;
dist[1]=0;
q.push(1);
st[1]=true;
while(q.size())
{
auto t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
for(int i=1;i<=2021;i++)
{
for(int j=1;j<=2021;j++)
{
if(i==j)
{
add(i,j,0);
continue;
}
else
{
if(abs(i-j)<=21)
{
int t=gcd(i,j);
int d=i*j/t;
add(i,j,d);
}
}
}
}
spfa();
cout<<dist[2021];
return 0;
}