[最短路回顾]

最短路

单源最短路:dijkstra

dijkstra用于解决单源最短路问题,即起点唯一,终点不唯一
适用于稠密图,算法时间复杂度\(O(n^2)\)
该算法要求图中不能有负环
通过从起始点向外扩散,不断进行松弛操作,dis[i]表示从起点到当前点的最短的路径长度

dijkstra的贪心策略用在最长路上是错误的!!!

朴素写法

其实,邻接矩阵存图效率过低 只是来枚举行和列来找到距离最近的

#include<bits/stdc++.h>
using namespace std;
#define clean(a, b) memset(a, b, sizeof(a))
const int inf=0x3f3f3f3f;
const int maxn=1e4+9;
int n,m,s,t;
int mp[maxn][maxn],dis[maxn],vis[maxn];
void dijkstra(int now)
{
    clean(vis,0);//标记是否被处理过
    for(int i=1;i<=n;i++)
    {
        dis[i]=mp[now][i];//初始距离
    }
    dis[now]=0;//当前点到他自己的最短距离是0
    vis[now]=1;//当前点被处理过了
    int temp,j,k;
    for(int i=1;i<=n;i++)
    {
        temp=inf;
        for(j=1;j<=n;j++)
        {
            if(!vis[j]&&temp>dis[j])//找有没有没被处理
            {
                k=j;
                temp=dis[j];
            }
        }//找一个要拿来松弛的中间点
        if(temp==inf) break;
        vis[k]=1;
        for(j=1;j<=n;j++)//对每一个点进行松弛操作
        {
            if(!vis[j]&&dis[j]>dis[k]+mp[k][j]);
            {
                dis[j]=dis[k]+mp[k][j];
            }
        } 
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int u,v,w;
    for(int i=1;i<=n;i++)//预处理把边权记成inf
    {
        for(int j=1;j<=n;j++)
        {
            mp[i][j]=inf;
        }
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        if(mp[u][v]>w) mp[u][v]=mp[v][u]=w;//无向图
    }
    scanf("%d%d",&s,&t);
    dijkstra(s);//从s开始搜
    if(dis[t]==inf) puts("-1");//没有路
    else printf("%d\n",dis[t]);//最短路
    return 0;
}

堆优化

堆优化的主要思想就是使用一个优先队列(就是每次弹出的元素一定是整个队列中最小的元素)来代替最近距离的查找,用邻接表代替邻接矩阵,这样可以大幅度节约时间开销
我们将结点的编号,到起点的距离存到一个struct中,然后每次将他推入优先队列,这样每次弹出的所代表的距离一定是最短的,如果一个结点到起点的距离发生了变化,那就一定是变得更短了,所以我们每次只取队首就是最优的,顺便标记一下,如果用过了就continue

复杂度\(O(2*E+V*logV)\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(a) ((a) & -(a))
#define clean(a, b) memset(a, b, sizeof(a))
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 9;

int _;

//========================================================================
struct edge
{
    int to,cost,next;
}e[maxn];
int top,head[maxn],vis[maxn],dis[maxn];
void init()
{
    top=0;
    memset(head,-1,sizeof(head));
}
void insert_(int u,int v,int c)
{
    e[top].to=v;
    e[top].cost=c;
    e[top].next=head[u];
    head[u]=top++;
}
struct node
{
    int num,dist;
    bool operator < (const node &x) const
    {
        return dist>x.dist;
    }
};
priority_queue<node>q;
//========================================================================
int main()
{
    init();
    int n,m,s,u,v,w;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        insert_(u,v,w);
    }
    for(int i=1;i<=n;i++) dis[i]=2147483647; 
    dis[s]=0;
    q.push(node{s,0});
    while(!q.empty())
    {
        node x=q.top();
        q.pop();
        int y=x.num;
        if(vis[y]) continue;
        vis[y]=1;
        for(int i=head[y];i!=-1;i=e[i].next)
        {
            if(dis[e[i].to]>dis[y]+e[i].cost)  
            {
                dis[e[i].to]=dis[y]+e[i].cost;
                q.push(node{e[i].to,dis[e[i].to]});
            }
        }
    }
    for(int i=1;i<=n;i++) 
    {
        printf("%d ",dis[i]);
    }
    printf("\n");
    return 0;
}

话说,dijkstra+配对堆 应该不常用得到吧……

spfa

适用于稀疏图,带有负权边的图,找负环(不能输出)
建一个队列,初始队列中只有起始点,再建立数组记录起始点到所有点的最短路径(把初始点赋为inf,自己到自己的距离为0)
用队列中的点刷新起始点到所有点的最短路
若刷新成功且被刷新点不在队列中,则把他添加到队列里

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(a) ((a) & -(a))
#define clean(a, b) memset(a, b, sizeof(a))
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 9;

int _;

//========================================================================
struct edge
{
    int to,cost,next;
}e[maxn];
int top,head[maxn],vis[maxn],dis[maxn];
void init()
{
    top=0;
    memset(head,-1,sizeof(head));
}
void insert_(int u,int v,int c)
{
    e[top].to=v;
    e[top].cost=c;
    e[top].next=head[u];
    head[u]=top++;
}
struct node
{
    int num,dist;
    bool operator < (const node &x) const
    {
        return dist>x.dist;
    }
};
queue<int>qu;
void spfa(int start)
{
    while(!qu.empty()) qu.pop();
    dis[start]=0;
    vis[start]=1;
    qu.push(start);
    while(!qu.empty())
    {
        int now=qu.front();
        vis[now]=0;
        qu.pop();
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int yy=e[i].to;
            int cost=e[i].cost;
            if(dis[yy]>dis[now]+cost) 
            {
                dis[yy]=dis[now]+cost;
                if(!vis[yy])
                {
                    vis[yy]=1;
                    qu.push(yy);
                }
            }
        }
    }
}
//========================================================================
int main()
{
    init();
    int n,m,s,u,v,w;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        insert_(u,v,w);
    }
    for(int i=1;i<=n;i++) dis[i]=2147483647; 
    spfa(s);
    for(int i=1;i<=n;i++) 
    {
        printf("%d ",dis[i]);
    }
    printf("\n");
    return 0;
}

多源最短路 :Floyd

暴力枚举每一个中转点
当然时间复杂度也是超级大 \(O(n^3)\) 算好了时间复杂度再用!!

for(int k=1;k<=n;k++)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i!=j&&j!=k&&k!=i)
            {
                if(f[i][k]+f[k][j]<=f[i][j]) 
                f[i][j]=f[i][k]+f[k][j];
            }
        }
    }
}
posted @ 2020-07-16 13:57  L·S·D  阅读(158)  评论(0编辑  收藏  举报