Dijkstra算法的改进(Dijkstra算法 + dfs)

博客介绍了Dijkstra算法求解最短路径的变体,指出原算法在复杂图中可能出错,需优化。可先记录所有最短路径,再用dfs遍历找出符合要求的路径。还通过PAT 1030 Travel Plan题目,展示了用Dijkstra加dfs求解最短路径及最少花费的思路和代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 我们在学习Dijkstra算法的时候可以知道求解最短路径不可能只是简单的让我们求解出从源点到其余顶点的最短路径,更多的是根据Dijkstra算法来进行进一步的改变,从而达到我们解决问题的目的,而在我们遇到的使用Dijkstra算法求解最短路径的往往也是相应的变体形式,在原来Dijkstra算法的基础上增加若干个的条件,比如最短路径上的点权之和、边权之和或者求解出存在多条最短下最短路径的条数

事实上还可能出现更加复杂的图的情况,假如我们使用Dijkstra算法的时候在某些情况下有可能会出错,所以我们需要对相应的算法进行优化,从而解决实际的问题

2. 我们可以这样考虑,可以先求解出所有的最短路径然后在所有的最短路径上求解出一条符合题目要求的最短路径,比如边权之和最大或者点权之和最大这些

① 使用Dijkstra算法算法记录所有最短路径,由于此时需要记录所有的最短路径,所以可能存在多条最短的路径那么这个时候使用原来的以为数组pre来存储一个前驱节点的方法就不适用了,考虑到这种情况我们可以将数组声明为可变长的数组vector类型,vector<int>  pre[maxSize],这样就可以存放节点v的所有的最短路径下的前驱节点,我们可以在更新最短路径的判断中来决定是否更新当前节点的前驱节点,当发现存在有多条最短路径的时候我们可以将其加入到vector数组中

if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
	d[v] = d[u] + G[u][v];
	pre[v].clear();
	pre[v].push_back(u);
else if(d[u] + G[u][v] == d[v]){
	pre[v].push_back(u);
}

② 遍历所有的最短路径

这里使用dfs来进行遍历,因为使用dfs并且借助于前驱的vector数组可以求解出源点到顶点v的所有最短路径中经历的节点,当使用dfs进行遍历的时候我们得到一条最短路径这个时候只需要根据题目给出的限制,是边权最短还是点权最大进行判断,得到最大点权或者边权直接遍历在dfs遍历过程中保存的最短路径即可获得

3. 下面通过一个题目来进一步的理解:题目来自于PAT 1030 Travel Plan

题目的大概意思是:有N个城市(编号为0-N - 1)M条道路(无向边),并且给出M条道路的距离属性与花费属性,现在给定起点s和终点d,求解出从起点s到终点的最短路径,最短距离,以及花费,注意如果存在多条最短路径的话那么则选择花费最小的那一条

① 思路分析:我们可以根据上面所说的Dijkstra算法加上dfs来进行解决,首先求解出pre数组然后对其所有的最短路径进行遍历找到最少花费的那一条路径

② pre的数组比较好求解,我们可以在更新最短的路径的时候同时更新pre数组即可,关键在于dfs遍历最短路径总花费最小的路径的求解,我们可以在dfs的时候借助于pre数组找到前驱节点,因为存在着多个前驱节点那么需要使用for循环来完成dfs的递归调用,下面是dfs的核心代码:

tempPath.push_back(v);
	for(int i = 0; i < pre[v].size(); i++){
		dfs(pre[v][i]);
	}
	tempPath.pop_back();

我们传进去dfs方法的参数是最终的那个顶点然后使用pre数组往前找,假如发现不是不是起点那么把当前的前驱顶点加入到临时路径中,而且需要在for循环的递归结束之后才能够删除临时路径中的最末尾的元素,这个点需要理解清楚,因为前驱节点是有可能存在多个的我们只有当所有当前的前驱节点都递归完成之后才可以将当前节点删除掉

测试数据如下:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

4. 下面是具体的代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm> 
#include<vector> 
using namespace std;
const int maxSize = 510;
const int INF = 1000000000;
int n, m, st, ed, G[maxSize][maxSize], cost[maxSize][maxSize];
int d[maxSize], minCost = INF;
bool vis[maxSize] = {false};
vector<int> pre[maxSize];
//tempPath为临时路径,  path为最优路径 
vector<int> tempPath, path;
void Dijstra(int s){
	fill(d, d + maxSize, INF);
	d[s] = 0;
	for(int i = 0; i < n; i++){
		int u = -1, min = INF;
		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[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
				d[v] = d[u] + G[u][v];
				pre[v].clear();
				pre[v].push_back(u);
			}else if(d[u] + G[u][v] == d[v]){
				pre[v].push_back(u);
			}
		}
	}	
}

void dfs(int v){
	if(v == st){
		tempPath.push_back(v);
		int tempCost = 0;
		for(int i = tempPath.size() - 1; i >0; i--){
			int id = tempPath[i], idNext = tempPath[i - 1];
			tempCost += cost[id][idNext];
		}
		if(tempCost < minCost){
			minCost = tempCost;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for(int i = 0; i < pre[v].size(); i++){
		dfs(pre[v][i]);
	}
	tempPath.pop_back();
}

int main(void){
	cin >> n >> m >> st >> ed;
	int u, v;
	fill(G[0], G[0] + maxSize * maxSize, INF);
	fill(cost[0], cost[0] + maxSize * maxSize, INF);
	for(int i = 0; i < m; i++){
		cin >> u >> v;
		cin >> G[u][v] >> cost[u][v];
		G[v][u] = G[u][v];
		cost[v][u] = cost[u][v];
	}
	Dijstra(st);
	dfs(ed);
	for(int i = path.size() - 1; i >= 0; --i){
		cout << path[i] << " ";
	}
	cout << d[ed] << " " << minCost;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值