《算法笔记》读书记录DAY_45

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

10.4.2Dijkstra算法

(接上篇)

下面通过两道例题,练习Dijkstra算法的运用。

题目1:

给出N个城市(城市用0,1,...,N-1编号),M条无向边,每条边代表连接两个城市的道路,边的权值代表其距离。每个城市中都有一定数目的救援小组,所有边的边权已知。现在给出起点和终点,求从起点到终点的最短路径条数及最短路径上的救援小组数目之和。

输入格式:

每个输入包含一个测试用例。对于每个测试用例,第一行给出4个正整数:城市数N(N<=500)、道路数目M、起点城市C1、终点城市C2。第二行给出N个整数,分别代表N个城市各自的救援小组数目。接下来M行分别给出每条道路的信息,每行包括三个整数,前两个表示该道路连接的两个城市编号,第三个整数表示该道路的距离。

输出格式:

对于每个输入,在一行内输出两个数字(中间用空格分隔):C1与C2间的最短路径数目、最短路径中救援小组之和的最大值。

输入样例:

5 6 0 2

1 2 1 5 3

0 1 1

0 2 2

0 3 1

1 2 1

2 4 1

3 4 1

输出样例:

2 4

思路:

该题使用无向图模型,首先为输入数据建立图模型,再用Dijkstra算法生成最短路径树即可得到起点C1到C2的最短路径。

但是基础的Dijkstra算法并不能完全解决这个问题,原因在于两点:题中要求输出最短路径的最大点权和,而基础的Dijkstra是没有考虑点权这个标尺的;在基础的Dijkstra中,起点S到另一点V只能生成一条最短路径,即没有考虑存在多条最短路径的情况,而题目中要我们输出最短路径数目。

要解决这两个问题,我们要对原Dijkstra算法进行两点小改动:

(1)为图模型新增点权。设置数组w[n],w[u]表示起点到顶点u的最短路径的最大点权和。初始化时令起点的w[]值为起点的权值,而其他顶点的w[]为0。

(2)要求最短路径数。设置数组num[n],num[u]表示从起点到顶点u的最短路径条数。初始时令起点的num[]为1,而其他顶点的num[]为0。

设置上面数组后,在Dijkstra中更新d[]的时候w[]和num[]只需跟着同步更新。

参考代码:

#include<iostream>
#include<vector>
#include<algorithm>
#define INF 0x3fffffff
using namespace std;

const int maxn=1001;
int n,m,c1,c2;             //n为顶点数,m为道路数,c1起点,c2终点
int d[maxn];               //记录起点到每个顶点的最短距离 
int weight[maxn];          //记录起点到每个顶点的最大点权和 
int num[maxn];             //记录起点到每个顶点的最短路径数目 
bool vis[maxn]={0};        //用来实现集合SET 

struct path {
	int v;                 //边的终点 
	int pathW;             //边权 
	path(){}
	path(int _v,int _pathW) {
		v=_v;
		pathW=_pathW;	
	}
};

struct graph {            //图的邻接表 
	int nodeW;            //点权 
	vector<path> Adj;     //记录每个顶点的边 
}G[maxn];

void Dijkstra() {
	fill(d,d+n,INF);
	d[c1]=0;                                              //初始化d[]
	fill(weight,weight+n,0);
	weight[c1]=G[c1].nodeW;                               //初始化weight[]
	fill(num,num+n,0);
	num[c1]=1;                                            //初始化num[]	 
	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) {                     //找到未访问顶点中d[]最小的
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1)                                     //找不到最小路径的邻接点,说明剩下的顶点与起点不连通 
			return;
		vis[u]=1;                                     //访问顶点u
		for(int j=0;j<G[u].Adj.size();j++) {		  //遍历每个u能到达的顶点v
			int v=G[u].Adj[j].v;
			if(vis[v]==0) {
				if(d[u]+G[u].Adj[j].pathW==d[v]) {        //找到一条相同路径
					if(weight[u]+G[v].nodeW>weight[v]) {
						weight[v]=weight[u]+G[v].nodeW;   //更新当前最大的weight[v] 
					}
					num[v]+=num[u];                       //更新num[] 
				}
				if(d[u]+G[u].Adj[j].pathW<d[v]) {
					d[v]=d[u]+G[u].Adj[j].pathW;          //优化d[v]
					weight[v]=weight[u]+G[v].nodeW;       //更新weight[] 
					num[v]=num[u];                        //到邻接点v的最短路径数目和u点相同 
				}
					
			}
		}
	}
}

int main() {
	int u,v,w;
	cin>>n>>m>>c1>>c2;                     //输入数据的第一行 
	for(int i=0;i<n;i++) {                 //输入每个顶点的点权  
		cin>>G[i].nodeW;
	}
	for(int i=0;i<m;i++) {                 //输入每条边和边权 
		cin>>u>>v>>w;
		G[u].Adj.push_back(path(v,w));
		G[v].Adj.push_back(path(u,w));
	}
  	Dijkstra();
  	cout<<num[c2]<<' '<<weight[c2]<<endl;   //输出c1到c2的最短路径数和其中的最大点权 
	return 0;
}

猜你喜欢

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