前置内容
迪杰斯特拉算法基础请看:迪杰斯特拉算法
算法优化方法
当数据量较大时, 直接使用迪杰斯特拉会超时, 这个时候可以使用优先队列对算法进行优化
迪杰斯特拉算法的每一轮, 都需要找一个未被访问且距离起点最近的点来进行操作, 这个寻找的过程需要遍历距离数组, 比较耗时; 当我们每次更新一个点的距离时, 可以将当前点与距离组合成pair, 放入优先队列, 优先队列以距离排序, 使得队首为距离最小元素, 这样, 每次取出队首, 就可以知道距离起始点最近的点, 大大优化计算时间。
题目链接
洛谷P2984 [USACO10FEB] Chocolate Giving S
程序示例
#include<bits/stdc++.h>
using namespace std;
// 定义一个较大的数表示无穷大,用于初始化距离数组
#define INF 0x3f3f3f3f
// 定义比较结构体,用于优先队列中元素的比较
// 重载 () 运算符,使得优先队列按照距离(pair 的第二个元素)从小到大排序
struct cmp
{
bool operator()(const pair<int, int> a, const pair<int, int> b)
{
return a.second > b.second;
}
};
// 定义结构体表示图中的边,包含目标节点 v 和边的长度 L
struct Node
{
int v;
int L;
};
// 定义常量 N 表示最大的节点数
const int N = 50005;
// 定义布尔数组 vis,用于标记节点是否已访问过
bool vis[N];
// 定义数组 dis,用于存储从起点到各个节点的最短距离
int dis[N];
int main()
{
// 读取农场数量 n,边的数量 m,询问的数量 b
int n, m, b;
cin >> n >> m >> b;
// 定义邻接表 g,用于存储图的结构,每个节点对应一个边的列表
vector<Node> g[n + 1];
// 定义优先队列 pq,用于实现迪杰斯特拉算法中的节点选择,按照距离从小到大排序
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> pq;
int u, v, l;
// 读取每条边的信息,包括两个端点 u, v 和边的长度 l,并将边加入邻接表
while(m--)
{
cin >> u >> v >> l;
g[u].push_back({v, l});
g[v].push_back({u, l});
}
// 迪杰斯特拉算法
// 将距离数组 dis 初始化为无穷大,表示初始时不知道到各个节点的最短距离
fill(dis, dis + n + 1, INF);
// 将起点(编号为 1 的农场)到自身的距离初始化为 0
dis[1] = 0;
// 将起点和其距离(0)加入优先队列,准备开始算法
pq.push({1, 0});
// 当优先队列不为空时,继续执行迪杰斯特拉算法
while(!pq.empty())
{
// 取出优先队列队首, 即距离起点最近的节点
int h = pq.top().first;
pq.pop();
// 如果该节点已经访问过,则跳过
if(vis[h]) continue;
// 标记该节点为已访问
vis[h] = 1;
// 遍历该节点的所有邻接边
for(auto t : g[h])
{
int v = t.v;
int L = t.L;
// 如果邻接节点未访问过,并且通过当前节点到邻接节点的距离更短
if(!vis[v] && dis[v] > dis[h] + L)
{
// 更新邻接节点的最短距离
dis[v] = dis[h] + L;
// 将更新后的邻接节点及其距离加入优先队列
pq.push({v, dis[v]});
}
}
}
// 处理 B 次询问,每次询问输出从起点到两个节点的最短距离之和
while(b--)
{
cin >> u >> v;
cout << dis[u] + dis[v] << endl;
}
return 0;
}
优先队列:priority_queue的内容, 请自行查询相关资料, 无需太过详细, 只需知道如何定义优先队列, 如何设定优先队列排序规则即可