跟着柳婼学姐学习的笔记ヾ(≧▽≦*)o
原题目: 1003 Emergency (25 分).
题意
一张地图,城市间由道路连接,并标有每个城市的救援队数量以及每条路的长度。
给出城市数N(≤500,编号0~N-1),道路数M,所在城市C1和要救援的城市C2。接着N个整数给出每个城市的救援队数量,接着M行给出城市对和距离(C1到C2至少有一条路)。
① 求C1到C2的最短路径数量,
② 最短路径上的救援队最多数量。
分析
- 最短路径问题(单源且正值)——Dijkstra算法。
- 在优化部分,增加:① 更新最短路径数量,② 更新点权值(城市救援队数量)。
CODE
#include <iostream>
using namespace std;
const int maxv = 510;
const int inf = 1000000000;
int n, m, c1, c2;
int G[maxv][maxv], weight[maxv]; //图,救援队数量
int dis[maxv], num[maxv], w[maxv]; //最短路径长度,最短路径数量,最大救援队数量(均从c1开始到各顶点)
bool vis[maxv]; //标记访问
void Dijkstra(int c1);
int main()
{
cin >> n >> m >> c1 >> c2;
for ( int i=0; i<n; i++ )
cin >> weight[i];
//建图
fill(G[0], G[0]+maxv*maxv, inf); //初始化
for ( int i=0; i<m; i++ ){
int a, b, c;
cin >> a >> b >> c;
G[a][b] = G[b][a] = c;
}
Dijkstra(c1);
printf("%d %d", num[c2], w[c2]);
return 0;
}
void Dijkstra(int c1){ //起点为c1
//初始化
fill(dis, dis+maxv, inf);
dis[c1] = 0;
w[c1] = weight[c1]; //救援队
num[c1] = 1; //c1到c1的最短路径数量
//遍历所有城市
for ( int i=0; i<n; i++ ){
//找到u使d[u]最小
int u = -1, mindis = inf;
for ( int j=0; j<n; j++ ){
if ( vis[j]==false && dis[j]<mindis ){
u = j;
mindis = dis[j];
}
}
//没找到u
if ( u==-1 ) return;
//找到u
vis[u] = true; //标记访问
//以u为中介点遍历其邻接点v,优化d[v]
for ( int v=0; v<n; v++ ){
if ( vis[v]==false && G[u][v]!=inf ){
if ( dis[u]+G[u][v]<dis[v] ){
dis[v] = dis[u] + G[u][v]; //优化距离
num[v] = num[u]; //起点到v的最短路径数量与到u的数量相同
w[v] = w[u] + weight[v]; //更新能到达v的救援队数量
}
else if ( dis[u]+G[u][v]==dis[v] ){ //多条最短路径
num[v] += num[u]; //从原来的路径到v + 从新路径到v
if ( w[u]+weight[v]>w[v] )
w[v] = w[u] + weight[v]; //要求救援队数量最多
}
}
}
}
}
CODE V2——BF解法
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int maxv = 510;
const int inf = 1000000000;
struct node{
int v, dis; //邻接点,邻接边的边权
};
vector<node> Adj[maxv]; //邻接表
int n, m, st, ed,weight[maxv]; //st和ed分别为起点和终点,weight[]记录点权
int d[maxv], w[maxv], num[maxv]; //w[]记录最大点权之和,num[]记录最短路径条数
set<int> pre[maxv]; //前驱
void Bellman(int s){
//初始化
fill(d, d+maxv, inf);
d[s] = 0;
w[s] = weight[s];
num[s] = 1; //s到s有1条路径
//循环n-1趟,生成d[]
for ( int i=0; i<n-1; i++ ){
for ( int u=0; u<n; u++ ){ //遍历每个顶点
for ( int j=0; j<Adj[u].size(); j++ ){
int v = Adj[u][j].v; //u的邻接点
int dis = Adj[u][j].dis; //邻接边的边权
if ( d[u]+dis<d[v] ){
d[v] = d[u] + dis;
w[v] = w[u] + weight[v];
num[v] = num[u]; //覆盖
pre[v].clear();
pre[v].insert(u);
}
else if ( d[u]+dis==d[v] ){ //不止一条最短路径
if ( w[u]+weight[v]>w[v] ){ //点权更大时
w[v] = w[u] + weight[v];
}
pre[v].insert(u);
//重新统计num[v]
num[v] = 0;
for ( auto it=pre[v].begin(); it!=pre[v].end(); it++ ){
num[v] += num[*it];
}
}
}
}
}
}
int main()
{
cin >> n >> m >> st >> ed;
for ( int i=0; i<n; i++ )
cin >> weight[i];
int u, v, wt;
for ( int i=0; i<m; i++ ){
cin >> u >> v >> wt;
Adj[u].push_back(node{v,wt});
Adf[v].push_back(node{u,wt});
}
Bellman(st);
printf("%d %d", num[ed], w[ed]);
return 0;
}