最短路问题分为单源和多源,源就是起点的意思,所以多源是多起点问题;

单源问题,要讨论边权

边权为正

使用Dijkstra算法

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 510;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int j=0;j<n;j++)
    {
        int t=-1;
        for(int i=1;i<=n;i++)
            if(!st[i]&&(t==-1||dist[t]>dist[i])) t=i;
        
        for(int i=1;i<=n;i++)
             dist[i]=min(dist[i],dist[t]+g[t][i]);
        st[t]=true;
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    while (m -- )
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    cout<<dijkstra()<<endl;
    return 0;
}

时间复杂度度为n^2 还有一个堆优化版本

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+10;
int h[N], e[N], w[N], ne[N], idx;
int dist[N];
bool st[N];
int n,m;

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()  // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    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(dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    memset(h, -1, sizeof h);
   cin>>n>>m;
   while(m--)
   {
       int a,b,c;
       cin>>a>>b>>c;
       add(a,b,c);
   }
   cout<<dijkstra()<<endl;
   return 0;
}

时间复杂度是mlogn;

边权为负

bellman-ford算法 这个算法时间复杂度是n*m;

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 1e6+10;
struct app{
    int a,b,c;
}a[N];
int dist[N],distup[N];
int n,m,k;
void bellman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++)
    {
        memcpy(distup,dist,sizeof dist);
        for(int i=0;i<m;i++)
        {
            dist[a[i].b]=min(dist[a[i].b],distup[a[i].a]+a[i].c);
        }
    }
}
int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<m;i++) cin>>a[i].a>>a[i].b>>a[i].c;
    
    bellman_ford();
    if(dist[n]>0x3f3f3f3f/2) puts("impossible");
    else cout<<dist[n];
}

优化版本是spfa算法

spfa算法的时间复杂度一般为m,最坏为n*m;

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int N = 1e6+10;
int h[N], e[N], w[N], ne[N], idx;
int dist[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
    queue<int>q;
    memset(dist,0x3f,sizeof dist);
    q.push(1);
    dist[1]=0;
    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])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j])
                {
                    st[j]=true;
                    q.push(j);
                }
            }
        }
    }
    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==0x3f3f3f3f) puts("impossible");
    else cout<<t;
}

spfa还可以求负环 代码如下

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int N = 1e6+10;
int h[N],ne[N],e[N],idx,w[N];
int dist[N],cnt[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool spfa()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
    {
        q.push(i);
        st[i]=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])
            {
                dist[j]=dist[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                if(!st[j])
                {
                    st[j]=true;
                    q.push(j);
                }
            }
        }
    }
    return false;
}
int main()
{
    memset(h, -1,sizeof h);
    cin>>n>>m;
    while (m -- )
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    if(spfa()) puts("Yes");
    else puts("No");
}

但是有边数限制的只能使用bellman-ford算法

小总结

dijstra堆优化算法和spfa算法是图宽搜,可以简单记忆中间部分

图宽搜的代码如下

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

多源问题

使用Floyd算法

这个算法原理是根据区间动态规划.

#include <iostream>
#include <cstring>
#include <algorithm>


using namespace std;
const int N = 5110,INT=1e9;
int g[N][N];
int n,m,q;
void floyd()
{
    for(int k=1;k<=n;k++)
       for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
             g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
int main()
{
    cin>>n>>m>>q;
    
    for(int i=1;i<=n;i++)
       for(int j=1;j<=n;j++)
          if(i==j) g[i][j]=0;
          else g[i][j]=INT;
    while (m -- )
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    floyd();
    while (q -- )
    {
        int a,b;
        cin>>a>>b;
        if(g[a][b]>=INT/2) puts("impossible");
        else cout<<g[a][b]<<endl;
    }
}

=============================================

以上就是图论里面最短路部分,如有错误希望大佬能够指点一二