题意:
条件:给出M条路、起点和终点以及每个点含有的救援队的数量。
要求:
1. 找出从起点到终点的最短路径的条数
2. 所有最低路径中能召集最多救援队的数量。
3. 能召集最多救援队的最短路径
思路:
求单源最短路径可以使用Dijstra算法。
复杂度为:500^2 ,不会超时。
Dijstra算法:
思想:
G(V,E)V表示图中所有的顶点,E表示图中所有的边。
对G(V,E)设置一个集合S,存放访问过的顶点,从集合V-S
中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S,
之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。
这样的操作执行n次,直到S中包含所有顶点。
参考:算法笔记
基本模板(找出从起点到其他各点的最短距离):
const int INF = 0x3f3f3f3f;
//以邻接表形式保存图,稀疏图用邻接表,稠密图用邻接矩阵
typedef struct{
int next;
int len;
}
vector<Node> V[N];
bool vis[N];//标记访问过的顶点
int dis[N];//记录从起点开始到其他各点的距离
void Dijstra(int s){
//起点到自身的距离为0
dis[s] = 0;
//循环n次
for(int i = 0; i < n; i++){
int u = -1;
int min = INF;
//选择与起点s的最短距离最小的一个未访问顶点(记为u)
for(int j = 0; j < n; j++){
if(!vis[j] && dis[j] < min){
u = j;
min = dis[j]
}
}
//如果找不到未被访问过的顶点,说明找不到其他与s相通的顶点,跳出循环
if(u == -1){
break;
}
//标记u为已访问
vis[u] = true;
//令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离
for(int j = 0; j < V[u].size(); j++){
int v = V[u][j].next;
int len = V[u][j].len;
//如果v已经访问过,则跳过该点
if(vis[v]){
continue;
}
//优化
if(dis[u] + len < dis[v]){
dis[v] = dis[u] + len;
}
}
}
扩展模板:
1、记录最短路径的条数:
求最短路径条数,增加一个保存最短路径条数的数组num[],令从起点到达顶点u
的最短路径条数为num[u],起点到自身的路径只有一条,所以num[s] = 1,其余
一开始都记为0,这样就可以在d[u] + len < d[v] (即可以使s到v的最短距离
d[v]更优)时,更新d[v]并且让num[v]继承num[u],而当d[u] + len == d[v]
(即有其他最短路径时),将num[u]加到num[v]上。
//增加num[]
int num[N];
void Dijstra(int s){
num[s] = 0;
//省略上面模板的重复内容
……
//优化
if(dis[u] + len < dis[v]){
dis[v] = dis[u] + len;
num[v] = num[u];
}else if(dis[u] + len == dis[v]){
num[v] += num[u];
}
}
2、新增点权或边权:
以点权为例:用weight[u]表示每个点含有的权,并增加一个w[u],表示从起点到
顶点u所能获得的所有点权,初始时,将w[s] = weight[s],这样就可以在
d[u] + len < d[v] (即可以使s到v的最短距离d[v]更优)时,更新d[v]并且让
w[v] = w[u] + weight[v],而当d[u] + len == d[v](即有其他最短路径时),
对比w[u] + weight[v] 是否大于w[v],若是则更新边权。
//增加weight[],w[]
int weight[N];
int w[N];
void Dijstra(int s){
w[s] = weight[s];
//省略上面模板的重复内容
……
//优化
if(dis[u] + len < dis[v]){
dis[v] = dis[u] + len;
w[v] = w[u] + weight[v];
}else if(dis[u] + len == dis[v]){
if(w[u] + weight[v] > w[v]){
w[v] = w[u] + weight[v];
}
}
}
3、记录最优路径
设置一个数组pre[N];用来记录路径上每个顶点的上一个顶点。之后从终点DFS遍历
一圈就可以得到最优路径。
//e为终点
void dfsPath(int e){
if(e!=s){
dfsPath(path[e]);
printf("%d ", e);
}else{
//当找到起点时,结束遍历
printf("%d", e);
return;
}
}
掌握以上知识就可以解决该问题了
满分代码:
#include <bits/stdc++.h>
using namespace std;
int n, m, s, d;
typedef struct
{
int val;
int len;
} Node;
const int N = 510;
const int INF = 0x3f3f3f3f;
vector<Node> V[N];
int weight[N];
int num[N];
int w[N];
int dis[N];
bool vis[N];
int pre[N];
void Dijkstra(int s)
{
num[s] = 1;
dis[s] = 0;
w[s] = weight[s];
for (int i = 0; i < n; i++)
{
int u = -1;
int min = INF;
for(int j = 0; j < n; j++){
if(!vis[j] && dis[j] < min){
u = j;
min = dis[j];
}
}
if(u == -1)return;
vis[u] = true;
for(int j = 0; j < V[u].size(); j++){
int v = V[u][j].val;
int len = V[u][j].len;
if(!vis[v] && dis[u] + len < dis[v]){
num[v] = num[u];
w[v] = weight[v] + w[u];
dis[v] = dis[u] + len;
pre[v] = u;
}else if(!vis[v] && dis[u] + len == dis[v]){
num[v] += num[u];
if(weight[v] + w[u] > w[v]){
w[v] = weight[v] + w[u];
pre[v] = u;
}
}
}
}
}
void dfs(int e){
if(e == s){
printf("%d",e);
return;
}else{
dfs(pre[e]);
printf(" %d",e);
}
}
int main(void)
{
cin >> n >> m >> s >> d;
for (int i = 0; i < n; i++)
{
scanf("%d", &weight[i]);
}
while (m--)
{
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
Node pb = {b, c};
Node pa = {a, c};
V[a].push_back(pb);
V[b].push_back(pa);
}
fill(dis, dis + N, 0x3f3f3f3f);
Dijkstra(s);
printf("%d %d\n",num[d],w[d]);
dfs(d);
cout << endl;
return 0;
}