最短路

朴素版Dijkstra算法(适用于稠密图)

思路

  1. 初始化距离数组和邻接矩阵(无穷大)
  2. 处理重边问题在输入取最小值
  3. 每次找到一个最小的未更新的答案点
  4. 将其连边答案全部更新

例题:Dijkstra算法朴素版

代码实现如下:

#include<bits/stdc++.h>
using namespace std;

const int N=520,INF=0x3f3f3f3f;
int n,m;
int g[N][N],dis[N];
bool st[N];

int dijkstra()
{
    dis[1]=0;//每个点到一号点的最短距离
    //如需求到s点的一开始将dis[s]定义为0即可
    for(int i=0;i<n-1;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
        {
            if(!st[j]&&(t==-1||dis[t]>dis[j])) t=j;//找到当前未更新的最小点
        }
        for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],dis[t]+g[t][j]);//将t连接的点都更新最短距离
        st[t]=true;
    }
    if(dis[n]==0x3f3f3f3f)return -1;//1~n之间不连通,无法构成路径,也就没有最短距离
    return dis[n];
}
int main()
{
    cin>>n>>m;
    fill(g[0],g[0]+N*N,INF);
    fill(dis,dis+N,INF);
    while(m--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        g[x][y]=min(g[x][y],c);//处理重边
    }
    cout<<dijkstra()<<endl;
    return 0;
}

堆优化版Dijkstra算法(适用于稀疏图)

优点:

  1. 堆可以动态维护一个集合中的最小值
  2. 堆动态支持插入,删除,修改一个数

优化部分:

for(int j=1;j<=n;j++)
        {
            if(!st[j]&&(t==-1||dis[t]>dis[j])) t=j;//找到当前未更新的最小点
        }
//每次寻找一个最小值可以用堆来维护一个最小值
//复杂度从O(n)->O(1)
////////////////////////////////////////////
for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],dis[t]+g[t][j]);//将t连接的点都更新最短距离
//每次将点更新在堆中修改复杂度为mlogm
//复杂度从O(n)->O(mlogm)
//////////////////////////////////////////////
//总体复杂度从O(n^2)->O(mlogm)

例题:Dijkstra算法堆优化版

代码实现:

#include<bits/stdc++.h>
#define PII pair<int,int>
using namespace std;

const int N=1e6+10,INF=0x3f3f3f3f;
int n,m;
int h[N],e[N],ne[N],idx;
int dis[N],w[N];
bool st[N];

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
    fill(dis,dis+N,INF);
    dis[1]=0;
    priority_queue<PII,vector<PII>,greater<PII> > heap;//优先队列默认大根堆,这样子写可以维护小根堆,pair<int,int>第一个值存数值,第二个值存下标
    heap.push({0,1});
    while(heap.size())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.second,distance=t.first;
        if(st[ver])continue;//每次处理均为最小值且未处理过的
        st[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>dis[ver]+w[i])//有更短路就替换
            {
                dis[j]=dis[ver]+w[i];
                heap.push({dis[j],j});//将新的方案加入到队列中
            }
        }
    }
    //队列为空即走完整个图
    if(dis[n]==INF)return -1;
    return dis[n];
}
int main()
{
    cin>>n>>m;
    fill(h,h+N,-1);//将头结点数组全部初始化成-1
    while(m--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        add(x,y,c);
    }
    cout<<dijkstra()<<endl;
    return 0;
}

SPFA算法(适用于稀疏图)

Bellman-Ford太呆

优点:

  1. 每次只更新与结点有关点的最小距离

例题:SPFA求最短路

代码实现:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int h[N], e[N], ne[N], w[N], idx;//稀疏图用邻接表来存图
int n, m;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int spfa()
{
    fill(dist, dist + N, INF);//先将每个到源点的距离初始化成正无穷
    dist[1] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;
    while (q.size())
    {
        int 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])//更新t结点的所有距离
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q.push(j);//更新的点第一次访问就进度,后面可能还能再利用其更新其他点
                    st[j] = true;
                }
            }
        }
    }
    return dist[n];
}
int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while (m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    int t = spfa();
    if (t == INF)//如果到n点还无法更新距离说明无法连通
        puts("impossible");
    else
        cout << t << endl;
    return 0;
}

posted @ 2022-02-05 15:06  snaliuu  阅读(63)  评论(0)    收藏  举报