L2-001 紧急救援(迪杰斯特拉应用)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

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

输出样例:

2 60
0 1 3

这个题目很热闹啊,又是最短路,最短路经过最短条数,又是每个城市都有一个权值,从起点到终点的权值最大,还要输出路径(心想这都是啥?我只会求最短路的长度,还是自己太菜了,没有对迪杰斯特拉有很透彻的理解,还是自己太菜)。所以写个博客补充一下知识。

这个题目是从起点最快(最短路)到达终点,但是还有另一个要求,经过节点的权值和要尽量大,还要输出路径。

普通的最短路就是套模板。这个输出路径就是用一个数组来找这个点的前驱节点,然后在输出路径的时候,就像链表式的,往前找前驱节点,就是下面这样 path[ ]数组是一个点的前驱节点,然后存到vector 中,倒序输出,就得到路径。  

void print()
{
	int i;
	vector<int>mp;
	for(i=e;i!=s;i=path[i])
		mp.push_back(i);	
	mp.push_back(i);
	
	reverse(mp.begin(),mp.end());
	for(int i=0;i<mp.size();i++)
	{
		if(i)
			cout<<' ';
		cout<<mp[i];
	}	
}

当然还有DFS回溯输出路径(我好菜,递归也没有学的很精),先递到起点,然后再往回回溯输点,这种思想要记住,对于一些输出前驱路径的要求DFS递归输出就可以了,下面代码:
 

 
//dfs回溯输出  
//X一开始传进终点 
void dfs(int x)
{
	if(x==s)
		printf("%d",x);
	else
	{
		dfs(path[x]);
		printf(" %d",x);
	}	
}

然后看这个最短路径经过路的条数,这个我也不是很懂(菜死了我,只会套板子),我们需要新开一个数组就是由原点到各个点的路径长度,就是过了几条边。迪杰斯特拉基本的东西就是由原点开始找到那个权值最小的那个临近点,将它放进最短路答案的集合中,再由这个点出发到其他的点,看看由原点到这个点,还是由这个新的出发点到这个点近,近的话,更新数据,近的话(以下这个操作我不是很明白,听大神说是 优化用的,不知道怎么搞的),就是近的话,由原点到这个点的路径边数变为上一个点(上一个点作为出发点找他周围的点;就是上一个点过来的到这个点了)路径边数  (举个例子0->1 长度是1,现在1->2 数据要更新,权值更新为d[pos]+map[pos][j] (注意),由原点到这个2点路径长度变为 1(注意),然后在下一次循环时候,这个条件!vis[j]&&d[j]>d[pos]+map[pos][j] 就不会满足(注意),去else if 那个条件进去,由原点到这个点的路径长度这时 1+1=2 更新了,不知道搞这样子是为啥,可能有更高级的用处,我辣鸡 不是很懂为啥这样做)。就是下面这个操作。好玄。   

for(int i=0;i<n;i++)   //这个不影响  
	{
		minv=inf;
		for(int j=0;j<n;j++)
		{
			if(!vis[j]&&minv>d[j])
			{
				minv=d[j];
				pos=j;
			}
		}
		
		vis[pos]=1;


	
		
		for(int j=0;j<n;j++)
		{
			
			if(!vis[j]&&d[j]>d[pos]+map[pos][j])
			{
				d[j]=map[pos][j]+d[pos];
				pathsum[j]=pathsum[pos];      
				
				valsum[j]=valsum[pos]+val[j];
				path[j]=pos;
			}
			else if(!vis[j]&&d[j]==d[pos]+map[pos][j])
			{
				pathsum[j]+=pathsum[pos];  
				
				if(valsum[j]<valsum[pos]+val[j])
				{
					valsum[j]=valsum[pos]+val[j];
					path[j]=pos;
				}
					
			}
					
		}
			
	}

 还有一个需求,(在每个点上都会有一个权值,经过时会加上),求出在最短路的基础上看看怎么使得这个权值最大。我们现在再来一个数组来存储由原点到各个点的权值,一开始初始条件就是每个城市每个点都是 原先的权值。就是上面的代码。这个还是比较好想的。

代码:

 

#include<iostream>
#include<stdio.h>
#include<string>
#include<vector>
#include<algorithm>
#include<string.h>
const int maxn=1005;
typedef long long ll;
using namespace std;
const int inf=1e8;

int map[maxn][maxn];
int d[maxn];           //到每个点经过路的权值  
int vis[maxn];

int val[maxn];       //每个点初始的权值 
int valsum[maxn];   //由起点到每个点的权值  
int path[maxn];			//这个点之前的点 
int pathsum[maxn];      //到每个点的经过路的条数 

int n,m,s,e;

void init()
{
	memset(vis,0,sizeof(vis));
	memset(path,0,sizeof(path));
	memset(pathsum,0,sizeof(pathsum));
	
	
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		if(i==j)
			map[i][j]=0;
		else
			map[i][j]=inf;
			
		valsum[i]=val[i];
	}	
} 


void jd()
{
	for(int i=0;i<n;i++)
	{
		d[i]=map[s][i];
	}
	
	pathsum[s]=1;
	
	d[s]=0;
	int minv=inf;
	int pos;
	
	for(int i=0;i<n;i++)
	{
		minv=inf;
		for(int j=0;j<n;j++)
		{
			if(!vis[j]&&minv>d[j])
			{
				minv=d[j];
				pos=j;
			}
		}
		
		vis[pos]=1;
		
		for(int j=0;j<n;j++)
		{
			
			if(!vis[j]&&d[j]>d[pos]+map[pos][j])
			{
				d[j]=map[pos][j]+d[pos];
				pathsum[j]=pathsum[pos];
				valsum[j]=valsum[pos]+val[j];
				path[j]=pos;
			}
			else if(!vis[j]&&d[j]==d[pos]+map[pos][j])
			{
				pathsum[j]+=pathsum[pos];
				if(valsum[j]<valsum[pos]+val[j])
				{
					valsum[j]=valsum[pos]+val[j];
					path[j]=pos;
				}
				
				
			}
			
		}
			
	}
}

void print()
{
	int i;
	vector<int>mp;
	for(i=e;i!=s;i=path[i])
		mp.push_back(i);	
	mp.push_back(i);
	
	reverse(mp.begin(),mp.end());
	for(int i=0;i<mp.size();i++)
	{
		if(i)
			cout<<' ';
		cout<<mp[i];
	}	
}



int main()
{
	
	cin>>n>>m>>s>>e;
	for(int i=0;i<n;i++)
		scanf("%d",&val[i]);
	
	init();
	
		
	int x,y,z;
	for(int i=0;i<m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		map[x][y]=map[y][x]=z;
	}
	
	jd();
	
	printf("%d %d\n",pathsum[e],valsum[e]);
	print();
	
	return 0;
}

(路还很长)。

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 迪杰斯特拉最短路径算法是一种用于解决带权有向图中单源最短路径问题的算法。该算法的基本思想是:从源点开始,依次确定到各个顶点的最短路径,直到所有顶点的最短路径都被确定。 具体实现过程如下: 1. 初始化:将源点到各个顶点的距离都设为无穷大,将源点到自身的距离设为。 2. 选择当前距离源点最近的未确定最短路径的顶点,将其标记为已确定最 ### 回答2: 迪杰斯特拉最短路径算法(Dijkstra's shortest path algorithm)是解决带权有向图或无向图中,从起点到终点的最短路径问题的经典算法之一。 该算法使用了贪心思想,每次从尚未确定最短路径的节点中选取一条距离起点最近的节点,通过该节点重新更新起点到其它未确定最短路径节点的距离,直到所有节点的最短路径都被确定。 具体实现如下: 1. 初始化:起点到自身的距离为0,其余节点的距离为无穷大。 2. 选择当前可到达且距离起点最近的节点,标记该节点为确定最短路径节点。 3. 更新该节点的邻居节点的距离,若经过当前节点到某个邻居节点的距离比已有距离短,则更新距离。 4. 重复2、3步骤,直到所有节点的最短路径都被确定。 在实际应用中,可以使用优先队列或堆等数据结构来优化算法实现,以提升算法效率。同时,该算法也具有一定的局限性,如不能处理存在负边权的图问题,需要使用另外的算法来解决。 总之,迪杰斯特拉最短路径算法是解决最短路径问题的高效算法之一,在实际应用中具有广泛的应用价值。 ### 回答3: 迪杰斯特拉最短路径算法是一种用于解决带有加权边的最短路径问题的算法。该算法的主要思想是从起点开始,通过计算所有节点到起点的距离,并选择距离最短的节点进行扩展。在扩展的过程中,更新已知的最短路径以及每个节点的父节点指向,直到扩展到终点节点或所有节点都被扩展为止。 在算法的实现过程中,首先需要将所有节点的距离初始化为无穷大,并将起点的距离设置为0。然后,将起点加入到一个未访问节点的集合中,并将其标记为已访问。接着,根据起点节点的邻居节点,更新它们的距离和父节点指向,并将它们加入到未访问节点的集合中。之后,从未访问节点的集合中选择距离最短的节点进行扩展,并重复之前的步骤,直到终点节点被扩展或所有节点都被扩展为止。 迪杰斯特拉最短路径算法的时间复杂度为O(n^2),其中n为节点的数量。在实际应用中,该算法也可以通过采用优先队列等数据结构来优化,从而达到更好的时间复杂度。 总之,迪杰斯特拉最短路径算法在现代计算机科学中应用广泛。它可以用于建立地图应用、路由算法、网络流量控制以及许多其他应用领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值