算法之最短路

Posted greed-vi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法之最短路相关的知识,希望对你有一定的参考价值。

最短路

我跟你讲SPFA已经死了好吧,SPFA+堆优又太难打,那就用dijkstra吧。(负权?我管它呢)

不加任何优化的裸dijkstra

一般般快吧,N^2,N=10000时可以卡过,很好打。

#include <bits/stdc++.h>
#define MAXN 1005
using namespace std;
long long LIS[MAXN][MAXN];//LIS数组存储图
long long dis[MAXN];//dis数组,存储最短长度值 
long long vis[MAXN];//vis[i]代表这个点有没有被当做源点去搜索过,1为有,0为没有。这样就不会重复搜索了。
long long N,M,S;
void dijkstra(long long x)//主函数,参数是源点编号
{
    long long start=x;//先从源点搜索  
	for(long long i=1;i<=N;i++) dis[i]=LIS[x][i];//先更新一遍
    dis[start]=0; 
	vis[start]=1;//标记源点已经搜索过
    for(long long i=1;i<=N-1;i++)
    {
        long long MINN=2147483647;//比较
        for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;//找到离源点最近的点,然后把编号记录下来,用于搜索,可优化
        vis[start]=1;        
        for(long long j=1;j<=N;j++) dis[j]=min(dis[j],dis[start]+LIS[start][j]);//以新的点来更新dis。
    }
}
int main()
{
    cin>>N>>M>>S;//N是点数,M是边数,S是出发点 
    for (long long i=1;i<=N;i++)for (long long j=1;j<=N;j++)LIS[i][j]=2147483647;
    for (int i=1;i<=N;i++) LIS[i][i]=0;
    for(long long i=1;i<=M;i++)
    {
        long long a,b,c;
        cin>>a>>b>>c;
        LIS[a][b]=min(LIS[a][b],c);//判重 
        //LIS[b][a]=min(LIS[a][b],c);//这是双向边 
    }
    dijkstra(S);//以S为源点。
    for(long long i=1;i<=N;i++) cout<<dis[i]<<" ";
	return 0;
}

堆优化+前向星存储dijkstra

因为普通dijkstra需要找最小值然后更新,所以对于

for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;

我们可以考虑用堆优化此过程,并且用链来存储,极大优化了内存

#include<bits/stdc++.h>
#define MAXN 100010
#define MAXM 200010
using namespace std;
long long last[MAXN],to[MAXM],nex[MAXM],v[MAXM],cnt=0,dis[MAXN];
bool vis[MAXN];  
long long n,m,s;
void add(int x,int y,int w)
{
    nex[++cnt]=last[x];//上一条边的编号 
    to[cnt]=y;//此边到哪个点 
    v[cnt]=w;//权值 
    last[x]=cnt;//以x开头的最后一条边的编号 
}
priority_queue< pair<int,int> > q;
void dijkstra(int s)
{
	for(int i=1;i<=n;i++) dis[i]=2147483647;//其实这样也可以,因为会自己更新的 
    dis[s]=0;
    q.push(make_pair(0,s));//放入初始节点 
    while(!q.empty())//还未遍历完 
	{
        int x=q.top().second;//堆头节点,最小节点编号 
		q.pop();//用过弹出 
        if(vis[x]) continue;//已拓展的就不要再做了 
        vis[x]=1;
        for(int i=last[x];i;i=nex[i])//拓展 
		{
            int y=to[i];//下一个点 
            if(dis[y]>dis[x]+v[i])
			{
                dis[y]=dis[x]+v[i];
                q.push(make_pair(-dis[y],y));//用相反数实现小根堆,放入堆 
            }
        }
    }
}
int main()
{
    cin>>n>>m>>s;
    for(int i=1;i<=m;i++)
    {
		int x,y,z;
		cin>>x>>y>>z;
    	add(x,y,z);//单向边 
	}
	dijkstra(s);
    for (int i=1;i<=n;i++) cout<<dis[i]<<" "; 
    return 0;
}

  

以上是关于算法之最短路的主要内容,如果未能解决你的问题,请参考以下文章

图论之最短路径floyd算法

贪心算法之最短路径(Dijkstra算法)

算法之最短路

图论之最短路径算法

数据结构之最短路径 [迪杰斯特拉算法]

leetcode之最短路刷题总结1