[最短路回顾]
最短路
单源最短路: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];
}
}
}
}