《算法笔记》读书记录DAY_47

CHAPTER_10  提高篇(4)——图算法专题

10.4.2Dijkstra算法

(接上篇)

下面通过一道例题来练习Dijkstra+DFS模板的运用。

题目:

有N个城市(编号为0-N-1)、M条道路(无向边),每条道路连接两个不同的城市,并给出M条道路的距离属性与花费属性。现在给定起点S与终点D,求从起点到终点的最短路径、最短距离及花费。注意:如果有多条最短路径,则选择花费最小的那条。

输入格式:

每个输入包含一个测试用例。对于每个测试用例,第一行给出四个整数:城市个数N、道路数目M、起点编号S、终点编号D。接下来给出M行,每行包括一条道路基本信息:连接城市C1、连接城市C2、道路距离Distance、道路花费Cost。其中所有整数都小于等于500,每个整数间用空格分隔。

输出格式:

对于每个输入,在一行内输出花费最小的最短路径,然后输出其最短距离和总花费,每个数字间用空格分隔,且输入结尾没有多余的空格。

输入样例:

4 5 0 3

0 1 1 20

1 3 2 30

0 3 4 10

0 2 2 20

2 3 1 20

输出样例:

0 2 3 3 40

思路:

首先根据输入数据构建无向图模型。接着使用上面所讲解的Dijkstra算法求解pre[]数组(这段模板可以直接默写)。

在获得记录了最短路径前驱结点的pre[]数组后,我们开始编写DFS函数。编写DFS的过程同样是套模板,我们设置全局遍历minCost记录路径花费的最小值(即第二标尺最优值),当递归到达叶子结点的时候计算当前路径的花费总和tempCost,如果当前tempCost小于minCost,则更新minCost的值,并将当前路径记为最短路径。

通过Dijkstra+DFS递归我们最终能获得所需要的最短路径path,然后逆序遍历输出即可。

参考代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn=1000;
const int INF=0x3fffffff;
int n,m,S,D;               //城市个数n、道路数目m、起点s、终点d
int d[maxn];               //记录最短路径 
bool vis[maxn]={0};
vector<int> pre[maxn];     //记录到达当前结点最短路径的前驱结点
vector<int> path,tempath;  //最优路径,临时路径
int minCost=INF; 

struct node {
	int v;                 //边的终点 
	int dis,cost;	       //距离distance,花费cost
	node(){}
	node(int _v,int _dis,int _cost) {
		v=_v;
		dis=_dis;
		cost=_cost;
	}
};

vector<node> Adj[maxn];    //图的邻接表 

void Dijkstra() {          //模板:Dijkstra获得pre[] 
	fill(d,d+n,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]==0&&d[j]<MIN) {
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1)
			return;
		vis[u]=1;
		for(int j=0;j<Adj[u].size();j++) {
			int v=Adj[u][j].v;
			if(vis[v]==0&&d[u]+Adj[u][j].dis<d[v]) {
				d[v]=d[u]+Adj[u][j].dis;
				pre[v].clear();
				pre[v].push_back(u);
			}
			else if(d[u]+Adj[u][j].dis==d[v]) {
				pre[v].push_back(u);
			} 
		} 
	}
}

void DFS(int v) {                                //DFS模板 
	if(v==S) {                                   //形成一条最短路径 
		tempath.push_back(v);
		int tempCost=0;                          //存放这条路径第二标尺:cost     
		for(int i=tempath.size()-1;i>0;i--) {    //计算这条路径上的cost和 
			int id=tempath[i],j;
			for(j=0;j<Adj[id].size();j++) {      //找到点tempath[i]到点tempath[i-1]的边 
				if(Adj[id][j].v==tempath[i-1])
					break;
			}
			tempCost+=Adj[id][j].cost;           //累加计算cost 
		}
		if(tempCost<minCost) {                   //如果当前最短路径的cost的更小
			minCost=tempCost;                    //更新最短路径花费 
			path=tempath;                        //更新最短路径 
		}
		tempath.pop_back();
		return;
	}
	tempath.push_back(v);
	for(int i=0;i<pre[v].size();i++) {
		DFS(pre[v][i]);
	}
	tempath.pop_back();
}


int main() {
	cin>>n>>m>>S>>D;
	int u,v,dis,cost;                          //临时存储每条道路信息 
	for(int i=0;i<m;i++) {                     //根据输入构建图           
		cin>>u>>v>>dis>>cost;
		Adj[u].push_back(node(v,dis,cost));
		Adj[v].push_back(node(u,dis,cost));
	}
	Dijkstra();
	DFS(D);
	for(int i=path.size()-1;i>=0;i--) {        //逆序输出最优路径 
		cout<<path[i]<<' ';
	}
	cout<<d[D]<<' '<<minCost;                  //输出最短路径值和最小花费 
	return 0;
}
 

还有一点需要提及,本题使用邻接矩阵实现比邻接表更为方便,空间条件上也允许使用邻接矩阵。邻接矩阵的解法和邻接表只有少许区别,留作读者自行思考。

猜你喜欢

转载自blog.csdn.net/jgsecurity/article/details/121115054