【知识点3】Dijkstra+DFS——Dijkstra的更加通用的模板⭐⭐⭐⭐⭐


今天也是为了cc,努力奋斗的一天ヾ(≧▽≦*)o

1. 引言

在引入了所谓的“第二标尺”的Dijkstra算法中,数组pre总是保持着最优路径,而显然需要在执行Dijkstra算法的过程中使用严谨的思路来确定何时更新每个节点v的前驱结点pre[v],实在是容易出错。

事实上,最简单的方法是:先在Dijkstra算法中记录下所有最短路径(只考虑距离),然后从这些最短路径中选出一条第二标尺最优的路径(因为在给定一条路径的情况下,针对这条路径的信息都可以通过边权点权很容易计算出来!)

2. 实现

(1)使用Dijkstra算法记录所有最短路径

由于此时要记录所有最短路径,因此每个节点就会存在多个前驱节点,这样原先pre数组只能记录一个前驱结点的方法不再适用。为了适应多个前驱的情况,不妨把pre数组定义为vector类型vector<int> pre[MAXV],这样对每个节点v来说,pre[v]就是一个变长数组vector,里面存放着节点v的所有可能产生最短路径的前驱节点。
注:对需要查询某个顶点u是否在顶点v的前驱中的题目,也可以把pre数组设置为set<int>数组,此时使用pre[v].count(u)来查询会比较方便

接下来就是考虑更新d[v]的过程中pre数组的变化:

  1. 如果d[u] + G[u][v] < d[v],说明以u为中介点可以使d[v]更优,此时需要令v的前驱节点为u。并且即便原来的pre[v]中已经存放了若干节点,此处也应该清空,然后再添加u
    if(d[u] + G[u][v] < d[v]){
    	d[v] = d[u] + G[u][v];
    	pre[v].clear();
    	pre[v].push_back(u);
    }
    
  2. 如果d[u]+G[u][v] == d[v],说明以u为中介点可以找到一条相同距离的路径,因此v的前驱节点需要在原先的基础上添加上u节点(而不必清空pre[v]),代码如下:
    if(d[u] + G[u][v] == d[v]){
    	pre[v].push_back(u);
    }
    

下面是使用Dijkstra求单源最短路径的通用模板:

vector<int> pre[MAXV];
void Dijkstra(int s){	//s为起点 
	fill(d,d+MAXV,INF);
	d[s] = 0;
	for(int i=0;i < n;i++){
		int u = -1;
		int MIN = INF;	//找到最小的d[u]
		
		for(int j=0;j<n;j++){
			if(vis[j] == false && d[j] < MIN){
				u = j;
				MIN = d[j];
			}
		}
		
		if(u == -1){
			return;
		} 
		
		vis[u] = true;
		
		for(int v = 0;v < n;v++){
			if(vis[u] == false && G[u][v] != INF){
				if(d[u] + G[u][v] < d[v]){
					d[v] = d[u] + G[u][v];	//优化d[v]
					pre[v].clear();	//清空pre[v]
					pre[v].push_back(u);	//令v的前驱为u 
				}else if(d[u] + G[u][v] == d[v]){
					pre[v].push_back(u);	//令v的前驱为u 
				} 
			}
		}
	}
}

(2)遍历所有最短路径,找出一条使第二标尺最优的路径

考虑如何写DFS的递归函数。

数据结构

首先,根据分析,必须要有的是:

  • 作为全局变量的第二标尺最优值optValue
  • 记录最优路径的数组path(使用vector来存储)
  • 临时记录DFS遍历到叶子节点时的路径tempPath(也使用vector存储)

递归边界

对于递归边界而言,如果当前访问的是叶子节点(其实就是路径的起点,思考为什么是路径的起点是递归边界?因为pre数组的原因),那么说明到达了递归边界,此时tempPath存放了一条路径。这时要做的就是对这条路径求出第二标尺的值value,并与optValue比较,如果更优,则更新optValue并把tempPath覆盖path

递归式

如果当前访问的节点是v,那么只需要遍历pre[v]中的所有节点并进行递归即可。

int optvalue;	//第二标尺最优值
vector<int> pre[MAXV];	//存放节点的前驱节点
vector<int> path,tempPath;	//最优路径,临时路径
void DFS(int v){	//v为当前访问结点 
	//递归边界
	if(v == st){	//如果到达了叶子节点st(即路径的起点) 
		tempPath.push_back(v);	//将起点st加入临时路径tempPath的最后面
		int value;	//存放临时路径tempPath的第二标尺值
		计算路径tempPath上的value值;
		if(value优于optvalue){
			optvalue = value;	//更新第二标尺最优值与最优路径
			path = tempPath; 
		}
		tempPath.pop_back() ;	//将刚加入的结点删除
		return; 
	}
	
	//递归式
	tempPath.push_back(v);		//将当前访问结点加入临时路径tempPath的最后面
	for(int i = 0;i< pre[v].size();i++){
		DFS(pre[v][i]);		//结点v的前驱结点pre[v][i],递归 
	}
	tempPath.pop_back();	//遍历完所有前驱节点,将当前结点v删除 
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值