Dijkstra算法

        首先,dijkstra算法是贪心算法中的一种。主要是运用于求最短路径的问题。Dijkstra算法的思路一般为,1构建一个图把所有点到点的距离存下来。2.先随便找某一点到其他点的距离。(一般默认从1开始)3.在这些距离中找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。

      

  根据这张图我们可以画出这样一个表

 

 

 

 

     

然后用dis来存储并根据情况变化

与点1最近的为2,接下来比对其余点到点2再到点1的距离与该点到1的距离,若比原来的小,就进行更改。

可以发现2到3的距离加上1到2的距离是比1到3的距离短,就将原来的无穷大改为   

10(1->2)+50(2->3)=60  再重复上面的步骤寻找与点2最近的点直到所有点都被用过

完整的变化图:

最后一排也就是我们要的结果。

可以看出1到2最短路径为10  到3为50  到4为30 到5为60.

下面借用蓝桥杯2021年的一道结果填空题来说明一下。题目是这样的:

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

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

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

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

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

构建mp图

void Init() {
    memset(mp, inf, sizeof(mp));//将所有的值定为无穷大
    for (int i = 1; i <=n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j)
                mp[i][j] = 0;//自己到自己的距离为0
            else
                if (abs(i - j) <= 21) 
                    mp[i][j] =mp[j][i]= i * j / gcd(i, j);//由题目所述当2个点的编号<=21时,他们之间的距离为这2个编号的最小公倍数。
        }
    }
}

Dijkstra算法

void dijkstra(int u) {
    memset(vis, 0, sizeof(vis));//清零
    memset(dis, 0, sizeof(dis));//清零
    for (int t = 1; t <= n; t++) {
        dis[t] = mp[1][t];//将1到其他点的距离存入dis数组
    }
    cout << endl;
    vis[1] = 1;//1到其他点的距离已用 标为1 
    for (int t = 1; t <= n; t++) {
        int minn = inf, temp=1;
         for (int i = 1; i <= n; i++) {
            if (vis[i]==0  && dis[i] < minn  && dis[i]!=0) {
                minn = dis[i];
                temp = i;
            }//寻找到1点最近的点。
    }
        vis[temp] = 1;//用过点的标1
        for (int i = 1; i <= n; i++) {
            if (mp[temp][i] + dis[temp] < dis[i]) {
                dis[i] = mp[temp][i] + dis[temp];//只要距离比原来的小就更改
            }
        }
    }
}

完整的代码为:

#include<bits/stdc++.h>
using namespace std;
int n = 2021;
long long mp[2022][2022];
long long vis[2022],dis[2022];//vis判断该点是否已被检索 
#define inf 0x3f3f3f3f//inf值给个较大的数
int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}//不知道为什么不能直接用_gcd函数,就手写一个
void Init() {
    memset(mp, inf, sizeof(mp));
    for (int i = 1; i <=n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j)
                mp[i][j] = 0;//自己到自己的距离为0
            else
                if (abs(i - j) <= 21) 
                    mp[i][j] =mp[j][i]= i * j / gcd(i, j); / 由题目所述当2个点的编号 <= 21时,他们之间的距离为这2个编号的最小公倍数。
        }
    }
}
void dijkstra(int u) {
    memset(vis, 0, sizeof(vis));//清零
    memset(dis, 0, sizeof(dis));//清零
    for (int t = 1; t <= n; t++) {
        dis[t] = mp[1][t];
    }//将1到其他点的距离存入dis数组
    cout << endl;
    vis[1] = 1; //1到其他点的距离已用 vis中标为1 
    for (int t = 1; t <= n; t++) {
        int minn = inf, temp=1;
         for (int i = 1; i <= n; i++) {
            if (vis[i]==0  && dis[i] < minn  && dis[i]!=0) {
                minn = dis[i];
                temp = i;
            }//寻找到1点最近的点。
    }
        vis[temp] = 1;//用过点的标1
        for (int i = 1; i <= n; i++) {
            if (mp[temp][i] + dis[temp] < dis[i]) {
                dis[i] = mp[temp][i] + dis[temp];//只要距离比原来的小就更改
            }
        }
    }
}
int main() {
    Init();
    dijkstra(n);
    cout << dis[n] << endl;
    return 0;
}

结果是

改题的答案

yjq 

 

    

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值