关于最短路

 

迪杰斯特拉最小堆优化理解与模板:

模板:

 

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是贪心,他只能找出来当前离源点最近的,却不会想以后会不会有负权边来更新当前最近的边,比如:

为啥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 链式向前星

 


待续……

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值