图的最短路径算法

Dijkstra算法

Dijkstra适用于边权值均为非负的图。

以下代码处理从A到G的最短路径

A B 5
A D 2
A E 7
B D 3
D E 1
B C 1
E C 9
E F 4
E G 3
F G 1

算法主要用到两个数组,一个dis[999]存储最短路径,一个check[999]存储每个节点是否更新

1,算法首先初始化dis,每个节点初始化无穷大,起点设置为0

2,然后找出dis中最近的节点Point A(第一次找到的是起点)

3,然后遍历图中可到达的节点Point B,判断dis[B]是否大于dis[A] + A到B的距离,然后更新dis

4,更新完后该节点已经使用完毕,就将他加入check数组中

循环2,3,4直到全部check之后,dis中的就是A到达所有节点的最短距离

#include <bits/stdc++.h>

using namespace std;

string begin = "A";
string end = "G";
map<string,int> dis;
set<string> check;

void Dijkstra(map<string,map<string,int>> m)
{
    //初始化dis
    for(auto elem : m) dis.insert({elem.first,99999});
    dis[::begin] = 0;

    while(check.size() != m.size())
    {
        //选择dis中最近的节点
        string min_s;
        int min_i = 99999;
        for(auto elem : dis)
        {
            if(check.find(elem.first) == check.end() && elem.second < min_i)
            {
                min_i = elem.second;
                min_s = elem.first;
            }
        }

        //循环更新最短路径
        for(auto elem : m[min_s])
        {
            if(dis[elem.first] > dis[min_s] + m[min_s][elem.first])
            {
                dis[elem.first] = dis[min_s] + m[min_s][elem.first];
            }
        }
        check.insert(min_s);
    }
}

int main()
{
    map<string,map<string,int>> m;
    for(int i = 0;i < 10;++i)
    {
        string x,y;
        int num;
        cin >> x >> y >> num;
        m[x].insert({y,num});
        m[y].insert({x,num});
    }
    Dijkstra(m);
    return 0;
}

Floyd算法

十分简单的一个算法

//循环处理
    for(int i = 0;i < date.size();++i)
    {
        for(int j = 0;j < date.size();++j)
        {
            for(int k = 0;k < date.size();++k)
            {
                if(grap[j][k] > grap[j][i] + grap[i][k])
                {
                    grap[j][k] = grap[j][i] + grap[i][k];
                }
            }
        }
    }
//去除自最短路径
for(int i = 0;i < date.size();++i) grap[i][i] = 0;

原理也十分简单:从a到b和a到c加上c到b一样,可以通过这一点,将c用所有节点代替,然后每次循环判断整个图,循环中更新,最后将所有自最短路径改为0即可

因为我一直用的邻接表,所以有一个建立邻接矩阵的过程

#include <bits/stdc++.h>

using namespace std;

void Floyd(map<string,map<string,int>> m)
{
    //建立邻接矩阵
    map<string,int> date;
    int temp = 0;
    for(auto elem : m) date.insert({elem.first,temp++});

    int grap[date.size()][date.size()];
    for(int i = 0;i < date.size();++i)
    {
        for(int j = 0;j < date.size();++j)
        {
            grap[i][j] = 9999999;
        }
    }
    for(auto elem1 : m)
    {
        for(auto elem2 : elem1.second)
        {
            grap[date[elem1.first]][date[elem2.first]] = elem2.second;
            grap[date[elem2.first]][date[elem1.first]] = elem2.second;
        }
    }

    //循环处理
    for(int i = 0;i < date.size();++i)
    {
        for(int j = 0;j < date.size();++j)
        {
            for(int k = 0;k < date.size();++k)
            {
                if(grap[j][k] > grap[j][i] + grap[i][k])
                {
                    grap[j][k] = grap[j][i] + grap[i][k];
                }
            }
        }
    }
    //去除自最短路径
    for(int i = 0;i < date.size();++i) grap[i][i] = 0;
    return;
}

int main()
{
    map<string,map<string,int>> m;
    for(int i = 0;i < 10;++i)
    {
        string x,y;
        int num;
        cin >> x >> y >> num;
        m[x].insert({y,num});
        m[y].insert({x,num});
    }
    Floyd(m);
    return 0;
}

Bellman算法

使用松弛的方法来选择最短路径

使用l[],r[],v[],dis[]四个数组,但是比floyd更简单

松弛代码为:

for(int i = 1;i <= n;++i)
{
    if(dis[l[i]] > dis[r[i]] + v[i]) dis[l[i]] = dis[r[i]] + v[i];
}

然后松弛边数-1的次数即可

#include <bits/stdc++.h>

using namespace std;

void Bellman(int *dis,const int *l,const int *r,const int *v,const int n,const int m)
{
    for(int k = 1;k < n;++k)
    {
        for(int i = 1;i <= m;++i)
        {
            if(dis[r[i]] > dis[l[i]] + v[i]) dis[r[i]] = dis[l[i]] + v[i];
        }
    }
}

int main()
{
    int n,m;
    cin >> n;
    int dis[n+1];
    fill(dis,dis + n + 1,9999);
    dis[1] = 0;
    cin >> m;
    int left[m + 1];
    int right[m + 1];
    int value[m + 1];
    for(int i = 1;i <= m;++i) cin >> left[i] >> right[i] >> value[i];
    Bellman(dis,left,right,value,n,m);
    for(int i : dis) cout << i << endl;
    return 0;
}
/*
7
10
1 2 5
1 4 2
1 5 7
2 4 3
4 5 1
2 3 1
5 3 9
5 6 4
5 7 3
6 7 1
*/

队列优化的Bellman算法(SPFA算法

思考一下,只有当前继节点松弛后,他才可能松弛,所以可以通过一个队列来优化算法,当节点松弛时将字节入队即可,当队空时就证明没有可松弛的节点了。

1,将起始节点入队

2,每次去头节点,松弛头节点的所有可到达节点,如果有一个节点松弛成功,且不在队中就入队

3,重复2直到队空

结束后即为结果

展示代码在下方

SPFA算法

SPFA可以用于处理带负权的图

SPFA算法根BFS的队列相似

用到两个数组,一个dis[999]存储最短路径,一个check[999]存储点是否在队列中

1,初始化dis,起始点设置为0,其他为无穷大,将起始点入队

2,更新头节点可到达节点的距离,如果成功更新则入队(如果已经在队中则不入)

3,头节点出队

4,循环2,3直到队为空

#include <bits/stdc++.h>

using namespace std;

string begin = "A";
map<string,int> dis;
set<string> check;

void SPFA(map<string,map<string,int>> m)
{
    for(auto elem : m) dis[elem.first] = 999999;
    deque<string> d;
    d.push_back(::begin);
    dis[::begin] = 0;
    check.insert(::begin);

    while(!d.empty())
    {
        string s = d.front();
        d.pop_front();
        check.erase(s);
        for(auto elem : m[s])
        {
            if(dis[s] + m[s][elem.first] < dis[elem.first])
            {
                dis[elem.first] = dis[s] + m[s][elem.first];
                if(check.find(elem.first) == check.end()) d.push_back(elem.first);
            }
        }
    }
}

int main()
{
    map<string,map<string,int>> m;
    for(int i = 0;i < 10;++i)
    {
        string x,y;
        int num;
        cin >> x >> y >> num;
        m[x].insert({y,num});
        m[y].insert({x,num});
    }
    SPFA(m);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值