最短路(SSSP与APSP)算法 上——Floyd与Dijkstra算法

 APSP问题,即求出每两对点的最短路

 SSSP问题,即求出给定起点s到其他所有点的最短路,也称单源最短路问题。

Floyd算法

Floyd–Warshall(简称Floyd算法)是一种著名的解决任意两点间的最短路径(APSP)的算法。由于Floyd算法是一种动态规划算法,所以Floyd是一个非常简单的三重循环,而且循环体内的语句也十分简洁。

  设d[k][i][j]定义成:只能使用第1号~第k号点作为中间媒介时,点i到点j之间的最短路径长度。

 则它有两种情况进行转移:

 1.如果最短路经过点k,那么 d[k][i][j]=d[k-1][i][k]+d[k-1][k][j]

 2.如果最短路不经过点k,那么 d[k][i][j]=d[k-1][i][j]

 综合起来就是:d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])

 再次观察动态转移方程: d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])

 可以发现每一个第k阶段的状态(d[k][i][j]),所依赖的都是前一阶段(即第k-1阶段)的状态(如d[k-1][i][j], d[k-1][i][k]和d[k-1][k][j])。
 用滚动数组空间优化:d[i][j] = min(d[i][j], d[i][k]+d[k][j])

for (k=1;k<=n;k++)   
	for (i=1;i<=n;i++)     
		for (j=1;j<=n;j++){
			if(i==k || i==j || k==j) 
				contiue;       
			if (d[i,k] + d[k,j] < d[i,j])
				d[i,j] = d[i,k]+d[k,j];      
		} 

松弛操作

在单源最短路中,设d[v]表示起点S到结点v的当前最短路,pre[v]表示S到v的当前最短路径中v点之前 的一个点的编号。

Relax(u, v, w) 

if(d[v]>d[u]+w(u,v)){
   d[v] = d[u]+w(u,v);
   pre[v] = u;
} 

松弛操作是改变最短路径和前趋的唯一方式。

Dijkstra算法

 Dijkstra算法是求单源最短路算法,采用的是一种贪心 的策略 。数组d来保存源点s到各个顶点的最短距离。

  Dijkstra算法是求单源最短路算法,采用的是贪心的策略,适用所有边的权值非负。Dijkstra算法中设置了一结 点集合T,从源结点s到集合T中结点的最终最短路径的 权均已确定,即对所有结点v∈T,有d[v]=(s,v)的权值。 算法反复挑选出其最短路径估计为最小的结点u∈V-T, 把u插入集合T中,并对离开u的所有边进行松弛。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000;
int dis[maxn],vis[maxn],head[maxn],cnt=1,n,m,s;
struct T{
	int u;
    int v;
    int w;
    int next;
}edge[maxn];
void Input(){
    cin >> edge[cnt].u;
    cin >> edge[cnt].v;
    cin >> edge[cnt].w;
    edge[cnt].next = head[edge[cnt].u];
    head[edge[cnt].u] = cnt;
    cnt++;
}
void Dijkstra(int s) {
	dis[s] = 0;
	for (int i = 1; i <= n; i++) {
   		int uu = 0, mind = 0x3f3f3f3f;
    	for (int j = 1; j <= n; j++)
      		if (!vis[j] && dis[j] < mind){
				uu = j; mind = dis[j];
			}
    	vis[uu] = true;
		for(int j = head[uu];j != -1;j = edge[j].next)
    		if(dis[edge[j].v] > dis[uu] + edge[j].w)
				dis[edge[j].v] = dis[uu] + edge[j].w;
  	}
}
int main(){
    memset(head,-1,sizeof(head));
    cin>>n>>m>>s;
    for(int i = 1;i <= n;i++)
    	dis[i] = INT_MAX;
    for(int i = 1;i <= m;i++)
        Input();
    Dijkstra(s);
    for(int i = 1;i <= n;i++){
    	if(s == i) cout<<0<<" ";
    	else cout<<dis[i]<<" ";
    }
    return 0;
} 

此代码能过弱化版模板题 P3371【模板】单源最短路径(弱化版)

时间复杂度为O(n2)。

但是对于标准版模板题 P4779 结果是这样的: R80861937
亟待优化

Dijkstra算法的优化

Dijkstra算法的优化主要基于以下两个方面:

 用邻接表代替邻接矩阵;

 堆优化:将当前不在T集合中的所有结点的d[ ]放到 一个小根堆中,这样就可以O(1)查找不在T集合且d[ ] 最小的结点u,从堆中删除u,并对离开u的所有边进 行松弛,更新在堆中的d[ ],手写堆时利用pos[i]记录顶点i在堆中的位置,时间复杂度为O((n+e)logn)。利 用STL中的堆,时间复杂度为O((n+e)loge)

最终代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000;
int dis[maxn],vis[maxn],head[maxn],cnt=1,n,m,s;
inline int FRead(){char c=getchar();int x=0,f=1;while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}return x*f;}
struct T{
    int u;
    int v;
    int w;
    int next;
}edge[maxn];
void Input(){
    edge[cnt].u = FRead();
    edge[cnt].v = FRead();
    edge[cnt].w = FRead();
    edge[cnt].next = head[edge[cnt].u];
    head[edge[cnt].u] = cnt;
    cnt++;
}
struct node{
	int dis;
	int pos;
	bool operator < (const node &x) const{
		return x.dis < dis;
	}
};
priority_queue<node> q;
void Dijkstra(){
	dis[s] = 0;
	q.push((node){0,s});
	while(!q.empty()){
		node tmp = q.top();
		q.pop();
		if(vis[tmp.pos]) continue;
		vis[tmp.pos] = 1;
		for(int j = head[tmp.pos];j != -1;j = edge[j].next){
    		if(dis[edge[j].v] > dis[tmp.pos] + edge[j].w){
				dis[edge[j].v] = dis[tmp.pos] + edge[j].w;
				if(!vis[edge[j].v])
					q.push((node){dis[edge[j].v],edge[j].v});
			}
		}
	}
}
int main(){
    memset(head,-1,sizeof(head));
    cin>>n>>m>>s;
    for(int i = 1;i <= n;i++)
    	dis[i] = INT_MAX;
    for(int i = 1;i <= m;i++)
        Input();
    Dijkstra();
    for(int i = 1;i <= n;i++)
    	printf("%d ",dis[i]);
    return 0;
} 

关于132行的cnt++,没有把握就不要写在131行(吧),每次都是复制之前写的链式前向星的代码,在弱化版和标准版被坑了两次,每次都坑了我有两个小时,因为自己的机子上能过,但是洛谷不能过,各个版本不一样是真的恶心

此代码能过此代码能过标准版模板题 P4779【模板】单源最短路径(标准版)

posted @ 2023-08-23 09:26  CultReborn  阅读(545)  评论(0)    收藏  举报