题目大意:
给出N个城市,M条无向边。每个城市中都有一定数目的救援小组,所有边的边权均有输入得到,现在给出起点和终点,求从起点到终点的最短路径条数以及最短路径上的救援小组数目之和。如果有多条最短路径,则输出数目之和最大的。
算法思路:
本题需要求解从C1到C2的最短路径上的相关信息,那么很自然联想到用Dijstra算法,此题基本上算的上是模板题目,直接套用Dijstra算法的具体过程即可,这里唯一的不同在于存在优化路径的第二标尺,也即是救援队伍数目。
先交代数据的存储:
这里使用node_weight[1000]表示每一个城市的救援的团队数目,G1000代表每一条路的长度,dis[1000]表示起点C1到所有点的最短距离,visited[1000]为标记访问数组,num[1000]为起点到终点最优路径的条数,rescue_teams[1000]为起点到终点最优路径上可以携带的最多营救团队数目
Dijkstra过程:
初始化dis[C1] = 0;rescue_teams[C1] = node_weight[C1];num[C1] = 1;
然后遍历N次,每次首先寻找到一个没有访问且距离起点最短的结点nearest,然后根据nearest结点优化整个图中起点到其他所有节点(没有访问)的最短距离,当通过nearest可以更短的到达j时,我们更新最短距离dis[j],num[j],rescue_teams[j]
,当距离相等时先更新路径条数num[j]
,然后判断通过nearest到j的点权是否更大,如果是更新最大点权rescue_teams[j]
,这里得注意,路径条数num只和最短距离有关.
注意点:
- 1、路径条数num只和最短距离有关,在根据点权作为第二约束条件的时候,无需根据点权来更新。
提交结果:
AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
int N,M,C1,C2;// 城市数目,道路数目,所在城市,目标城市
int node_weight[1000];// 点权,表示每一个城市的救援的团队数目
int G[1000][1000];// 边权,代表每一条路的长度
int dis[1000];// 起点C1到所有点的最短距离
bool visited[1000];// 标记访问数组
int num[1000];// 最优路径的条数
int rescue_teams[1000];// 最多营救团队数目
void Dijkstra(){
fill(dis,dis+1000,0x3fffffff);
dis[C1] = 0;//起点到起点的最短距离
num[C1] = 1;// 初始路径条数为1,C1到自身为一条路径
rescue_teams[C1] = node_weight[C1];// 初始路径的点权之和为C1的点权
// 循环N次
for (int i = 0; i < N; ++i) {
// 首先找到一个距离C1最近的未访问的节点
int nearest = -1,min_dis = 0x3fffffff;
for (int j = 0; j < N; ++j) {
if(!visited[j]&&min_dis>dis[j]){
nearest = j;
min_dis = dis[j];
}
}
// 没有找到,说明后面的点不在连通
if(min_dis==0x3fffffff) return;
// 找到距离C1最近的点了,根据nearest更新C1到其他未访问的点的距离
visited[nearest] = true;
for(int j=0;j<N;++j){
if(!visited[j]&&G[nearest][j]!=0){
if(dis[j]>dis[nearest]+G[nearest][j]){
dis[j] = dis[nearest]+G[nearest][j];
num[j] = num[nearest];
rescue_teams[j] = rescue_teams[nearest] + node_weight[j];
} else if(dis[j]==dis[nearest]+G[nearest][j]){
// 路径长度相同,但是携带的救援团队更多
num[j] += num[nearest];
if(rescue_teams[j]<rescue_teams[nearest]+node_weight[j]){
rescue_teams[j] = rescue_teams[nearest] + node_weight[j];
}
}
}
}
}
}
int main()
{
scanf("%d %d %d %d",&N,&M,&C1,&C2);
for (int i = 0; i < N; ++i) {
scanf("%d",&node_weight[i]);
}
for(int i=0;i<M;++i){
int a,b,length;
scanf("%d %d %d",&a,&b,&length);
G[a][b] = G[b][a] = length;
}
Dijkstra();
printf("%d %d",num[C2],rescue_teams[C2]);
return 0;
}
这里再给出Bellman-Ford算法的版本:
#include<cstdio>
#include<vector>
#include<unordered_set>
using namespace std;
struct Node{
int des,dis;//邻接边的目标节点和边权
Node(int _des,int _dis){
des = _des;
dis = _dis;
}
};
vector<Node> Adj[1000];// 邻接表
int N,M,C1,C2;// 城市数目,道路数目,所在城市,目标城市
int node_weight[1000];// 点权,表示每一个城市的救援的团队数目
int dis[1000];// 起点C1到所有点的最短距离
int num[1000];// 最优路径的条数
int rescue_teams[1000];// 最多营救团队数目
unordered_set<int> pre[1000];// 保存所有节点的前驱节点
void Bellman(int start){
// 初始化
fill(dis,dis+1000,0x3fffffff);
rescue_teams[start] = node_weight[start];
num[start] = 1;
dis[start] = 0;
// N-1轮
for (int i = 0; i < N - 1; ++i) {
// 每一轮遍历所有的顶点的所有邻边
for (int j = 0; j < N; ++j) {
for (int k = 0; k < Adj[j].size(); ++k) {
int des = Adj[j][k].des;// 目标点
int d = Adj[j][k].dis;// 边权
if(dis[j]+d<dis[des]){
// 如果到达des的路径可以更短,更新信息
dis[des] = dis[j]+d;
num[des] = num[j];// 覆盖路径条数
rescue_teams[des] = rescue_teams[j] + node_weight[des];
pre[des].clear();
pre[des].insert(j);
}else if(dis[j]+d==dis[des]){
// 如果到达des的路径相同
if(rescue_teams[des]<rescue_teams[j] + node_weight[des]){
rescue_teams[des] = rescue_teams[j] + node_weight[des];
}
// 新增前驱,同时计算到达des的最短路径条数
pre[des].insert(j);
num[des] = 0;
unordered_set<int>::iterator it;
for(it=pre[des].begin();it!=pre[des].end();++it){
num[des] += num[*it];
}
}
}
}
}
}
int main(){
scanf("%d %d %d %d",&N,&M,&C1,&C2);
for (int i = 0; i < N; ++i) {
scanf("%d",&node_weight[i]);
}
int a,b,length;
for(int i=0;i<M;++i){
scanf("%d %d %d",&a,&b,&length);
Adj[b].emplace_back(a,length);
Adj[a].emplace_back(b,length);
}
Bellman(C1);
printf("%d %d",num[C2],rescue_teams[C2]);
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。