迪杰斯特拉最小堆优化理解与模板:
模板:
void dij()
{
bool vis[MAX];
memset(vis,0,sizeof(vis));
int i;
d[1]=0;
for(i=2;i<=n;i++)
{
d[i]=INF;
}
p.push(make_pair(d[1],1));
while(!p.empty())
{
pi u=p.top();
p.pop();
int x=u.second;
if(vis[x]) continue;
vis[x]=1;
for(i=1;i<=n;i++)
{
if(!vis[i] && map[x][i]!=INF)
{
if(d[x]+map[x][i] < d[i])
{
d[i]=d[x]+map[x][i];
p.push(make_pair(d[i],i));
}
}
}
}
}
为啥想到用最小堆?因为每次都是找与源点最近的,vis为0的点,想到了用优先队列找的快一点
细节:d数组用pair形式表示,pair的first是距离,second是点的下标。pair跟结构体差不多,如果用结构体写就是
struct XX
{
int dis,num;//dis记录num点与源点的距离
} d[MAXN];
之前想过一个问题,就是既然一个点出栈了,那么他就不可能再进栈了,因为出栈的前提是他已经被选为当前离源点最近的点,经过松弛操作之后又出栈,既然他已经是最近了,也就不会有别的点来松弛他,那么他就不会再进栈了,也就是说一个点只进栈一次,那么为啥还需要用vis判断呢?
问题出在:程序是一旦一个点被其他点松弛,它就进栈,为啥呢?因为说不定此时他就变成了离源点最近的点,它要跟其他的点竞争啊。。但是问题就是,这个点被多次松弛多次进栈但是每次都没轮到他出栈。如图:
如果两个点之间是不去重的有向边的话,即a->b跟b->a不一样时,我觉得不能用链式向前星。。因为它是以某个点为起点,与之相连接的边是谁谁谁,但是我不没办法知道以某个点为终点的边都是谁。。。并且,遇到去重问题貌似也不能解决。反正光用最小堆优化就完事了,已经很快了。
BF算法与SPFA理解
BF算法与Dij区别:
- BF的重点在于边,Dij在于点。
- Dij每次找的是离源点最近的点,然后松弛与这个点相连的边。循环v-1次,就找到了所有V个点的最短路径。
- BF每次都遍历所有的边,对于每条边,他都会判断一下这条边能不能让这条边的终点离源点更近一些,能的话就更新。
Dij不能有负权边,因为Dij是贪心,他只能找出来当前离源点最近的,却不会想以后会不会有负权边来更新当前最近的边,比如:
它会把dis[B]确定为1,而实际上经过c->b,dis[B]= -8,这都是因为贪心造成的。而BF无所谓,因为他是扩散型的,下文会说。
这个遍历为啥是V-1次,别的博客没见一个说为啥的,我想了两天,终于有个比较不错的理解
从第一次开始,用与源点直接相连的边(一条边直接相连,就叫它亲密度为1吧)来松弛与源点直接连接的点到源点的距离,然后更新了这些点的dis[]
第二次,只有亲密度为1的边使用过了,亲密度为2的边才有用武之地,比如说A->B->C,显然只有B点的dis【】从无穷变成权值,B->C才有用处,不然dis[B]存的是无穷,那肯定不能借助B->C这条边来松弛啊。
第三次,…………
那么假如说这个图是一个长串,有V个点,那么第V个点与源点的亲密度就是V-1(中间隔了V-1条边),也就是其他文章里说到的为啥是顶多循环V-1次就能求出最短路径。
实际运行时BF采取的是暴力,他实际上这V-1次每次都遍历所有的边,而他应该只遍历与他亲密度为V-1的边就行了,做了嫩(俺是河南人)多的无用功,比如第一次循环,它只能松弛与源点直接相连的边,而他却把所有边都遍历了一遍,浪费时间...
为了解决这个问题,可以用SPFA,哪个点被更新了,再去更新与这个点相连的点的dis[],犯不着每次都遍历所有的边。
转载俩模板:
https://www.cnblogs.com/xzxl/p/7246918.html
https://www.cnblogs.com/shadowland/p/5870640.html 链式向前星
待续……