Dijkstra
最短路径
dijkstra
不可解决有负权的图
但是若是没有负权的话,最好用这个,而不要用spfa,因为spfa的时间复杂度极其不稳定
对于稀疏图来说,spfa的时间复杂度确实是要比dijkstra要低
但,若是稠密图的话,spfa的时间复杂度就极其不可观了
所以,dijkstra的重要性显然
1.定义
不可解决有负权边的图
时间复杂度为O(n2)
经过堆优化的dijkstra的时间复杂度为O(nlogn)
2.算法描述
贪心的思想(这是因为这个思想,所以判不出来负权边--(咳咳,个人之见,逃~)
以起始点为中心向外层层扩展(广搜的思想),直到扩展到终点为止
基本思想:
引入两个集合S和U。
S:记录已求出最短路径的顶点
U:记录未求出最短路径的顶点
(能到源点的所有点)
操作:
1.初始时,S集合中只包含源点s
U集合中包含除s以外的其他顶点 且 U中顶点的距离为“起点s到该顶点的距离”
(若相连,即为边权值;若不相连,则设其为无穷大)
2.从U中选出“距离最短的顶点k”,将k加入s中,并将k从U集合中移出
3.用新加入的顶点k来更新U集合中各个顶点到起点s的距离
之所以更新U中顶点的距离,是因为 上一步中确定了k是求出最短路径的顶点,从而可以用k来更新其他的值
4.重复2.3,直到遍历完所有点
(假装已经会了)
普普通通的dijkstra
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; int map[110][110];//这就是map数组,存储图 int dis[10010];//dis数组,存储估计值 int book[10010];//book[i]代表这个点有没有被当做源点去搜索过,1为有,0为没有。这样就不会重复搜索了。 int n,m; void dijkstra(int u)//主函数,参数是源点编号 { memset(dis,88,sizeof(dis));//把dis数组附最大值(88不是十进制的88,其实很大) int start=u;//先从源点搜索 book[start]=1;//标记源点已经搜索过 for(int i=1; i<=n; i++) { dis[i]=min(dis[i],map[start][i]);//先更新一遍 } for(int i=1; i<=n-1; i++)//源点不用 { int minn=9999999;//这里的minn不是题解上的minn,这代表的是最近点到源点的距离,start才代表最近的点、 for(int j=1; j<=n; j++) if(book[j]==0 && minn>dis[j]) { minn=dis[j]; start=j;//找到离源点最近的点,然后把编号记录下来,用于搜索。 } book[start]=1; for(int j=1; j<=n; j++) dis[j]=min(dis[j],dis[start]+map[start][j]);//以新的点来更新dis。 } } int main() { cin>>n>>m; memset(map,88,sizeof(map)); for(int i=1; i<=m; i++) { int a,b,c; cin>>a>>b>>c; map[a][b]=c; }//有向图吧 for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(i==j) map[i][j]=0;//仅是初始化 dijkstra(1);//以1为源点。 for(int i=1; i<=n; i++) cout<<dis[i]<<" "; }
(普普通通的dij也几乎过不了什么,还得加上一个链式前向星)
堆优化的dijkstra
1.基本思想
//堆优化dijkstra 边权不可为负
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
inline int read()//快读
{
int sum = 0,p = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '0')
p = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
(sum *= 10) += ch - '0';
ch = getchar();
}
return sum * p;
}
const int maxn = 1e5 + 5;
const int maxm = 2e5 + 5;
int n,m,s;//n为点的个数,m为变得个数,s为源点
int tot,dis[maxn],wei[maxm];//dis为i点到源点的距离,wei为边权
int head[maxn],to[maxm],nxt[maxm];//链式前向星
bool mrk[maxn];//标记该点是否被加入到最短路径的生成树中(0-没有,1-有)
priority_queue< pair<int,int> > q;
/*
优先队列:默认是大根堆
pair第一维为dis[i]的相反数(这样就可以变成小根堆啦)
第二维为节点编号
*/
void dijkstra()
{
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;//dis初始化 起点为0,其余为正无穷
q.push(make_pair(0,s));
while(q.size())
{
int x = q.top().second;
q.pop();//取堆顶
if(mrk[x])
continue;
mrk[x] = true;
for(int i = head[x];i;i = nxt[i])
{//扫描所有出边
int y = to[i];
int z = wei[i];
if(dis[y] > dis[x] + z)
{
dis[y] = dis[x] + z;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
n = read(),m = read(),s = read();
int x,y,z;
for(int i = 1;i <= m;i++)
{
x = read(),y = read(),z = read();
wei[++tot] = z;
nxt[tot] = head[x];
to[tot] = y;
head[x] = tot;
}//构建邻接矩阵 (通常可用add函数)
dijkstra();
for(int i = 1;i <= n;i++)
printf("%d ",dis[i]);
return 0;
}
(我应该会回来补一补的qwq...)