47. 参加科学大会(第六期模拟笔试) (kamacoder.com)
上个题我们解决了迪杰斯特拉算法求解最短路径,但是还存在一个问题:
对于稠密图来说,迪杰斯特拉算法没有什么问题,可是稀疏图里,比如我们的点数远远超过边数,而我们还一个点一个点的去检查是否有些浪费时间呢?
就像之前的最小生成树的算法有存储点状态的Prim算法和存储边状态的K算法一样,我们的最短路径算法也应该去考虑去存储边的状态。
还记得我们刚开始图论题目时两种构建图的方法吗?是的,就是邻接矩阵和邻接表,其中邻接矩阵就是将两个节点都放进序号,而邻接表则是每一个点后面放一个链表,对于稀疏图来说,邻接表无疑是更好的选择。
但是对于最短路径来说存在一个问题就是:我们不仅仅是要记录所有的边,还需要去记录边的权值,且需要去根据边的权值进行一个选择,也就是出现一个排序的需求。我们在这里利用优先队列的有序性来解决,当然,我们要自定义一个比较器类。
其他的思路是不变的,主要的区别就在于加入了一个优先队列来根据边的权值自动排序以及用邻接表来构建图。
#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <climits>
using namespace std;
class mycomparison {
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
return lhs.second > rhs.second;
}
};
struct Edge{
int to;
int val;
Edge(int t,int v):to(t),val(v){}
}
int main(){
int n,m,s,e,v;
cin>>n>>m;
vector<list<Edge>> grid(n+1);
while(m--){
cin>>s>>e>>v;
grid[s].push_back({e,v});
}
vector<int> minDist(n+1,INT_MAX);
vector<bool> visited(n+1,false);
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pq;
pq.push_back({1,0});
minDist[1]=0;
while (!pq.empty()) {
pair<int, int> cur = pq.top(); pq.pop();
if (visited[cur.first]) continue;
visited[cur.first] = true;
for (Edge edge : grid[cur.first]) {
if (!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]) {
minDist[edge.to] = minDist[cur.first] + edge.val;
pq.push(pair<int, int>(edge.to, minDist[edge.to]));
}
}
}
if (minDist[end] == INT_MAX) cout << -1 << endl;
else cout << minDist[end] << endl;
}
现在让我们来接触第二种最短路径的算法:贝尔曼福德算法(是这样读吗)
贝尔曼福德算法的出来就是为了弥补迪杰斯特拉算法不能处理带有负权值的图的问题的,为什么迪杰斯特拉算法不能处理带负值的问题?
这个就是贪心算法的局限性:无法更新以及走过路径的最短路径,对于有负权值的图来说,往往会忽略一些全局上看更优秀的方案。
而贝尔曼福德算法则可以处理这一情况,具体如何操作呢?
所谓的松弛,其实本质上就是根据当前节点到该节点的权重总和与该节点的最小权重作比较实现更新,我们把除了源节点以外的所有节点都更新一次就可以得到对于所有点来说的最小权重,而不是像贪心算法一样去遍历相邻节点之间的最小权重。
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main(){
int n,m,s,t,v;
cin>>n>>m;
vector<vector<int>> grid;
while(m--){
cin>>s>>t>>v;
grid.push_back({s,t,v});
}
vector<int> minDist(n+1,INT_MAX);
minDist[1]=0;
for(int i=1;i<n;++i){
for(vector<int>& edge:grid){
int from=edge[0],to=edge[1],val=edge[2];
if(minDist[from]!=INT_MAX&&minDist[to]>minDist[from]+val){
minDist[to]=minDist[from]+val;
}
}
}
if(minDist[n]==INT_MAX)cout<<"unconnected"<<endl;
else cout<<minDist[n]<<endl;
}