注:本篇博客的思想来自于yxc大佬的视频讲解,按照大佬的思想自己敲了一下。
朴素Dijkstra算法
题目:Dijkstra求最短路 I
题意:该题题意就是,给你一个图,让你求出点1到n的最短路。
思路:由于该题所有边的权值都是正数,而且是一个稠密图,便比较多而且,点比较少,所以比较适合朴素的Dijstra算法。时间复杂度O(n^2)。别的算法也可以,该题没有卡spfa算法,就是哪一个稍微比较好的一点的问题。
下面是朴素Dijkstra算法,上面是spfa算法。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 510;
int mp[maxn][maxn], vis[maxn], dis[maxn];
int n, m;
void Dijstra(){
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for(int i = 1; i < n; i++){
int index = -1;
for(int j = 1; j <= n; j++){
if(!vis[j] && (index == -1 || dis[index] > dis[j])){
index = j;
}
}
vis[index] = 1;
for(int j = 1; j <= n; j++){
dis[j] = min(dis[j], dis[index] + mp[index][j]);
}
}
}
int main(){
cin>>n>>m;
memset(mp, 0x3f, sizeof mp);
for(int i = 1; i <= m; i++){
int a, b, c;
cin>>a>>b>>c;
mp[a][b] = min(mp[a][b], c);
}
Dijstra();
if(dis[n] == 0x3f3f3f3f)cout<<-1<<endl;
else cout<<dis[n]<<endl;
return 0;
}
堆优化Dijkstra算法
题目:Dijkstra求最短路 II
思路:本题的所有边的权值全部为正值,所以还可以接着用Dijkstra算法,由于点和边的个数都是1~1e5,属于稀疏图,可以用堆优化的Dijkstra算法。时间复杂度O(m*log(n))
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
to[tot] = b;
w[tot] = c;
nex[tot] = h[a];
h[a] = tot++;
}
int n, m, dis[maxn], vis[maxn];
typedef pair<int, int> PII;
int Dijkstra(){
memset(dis, 0x3f, sizeof dis);
priority_queue<PII>q;
dis[1] = 0;
q.push({0, 1});
while(q.size()){
PII tmp = q.top();
q.pop();
//cout<<tmp.second<<endl;
if(vis[tmp.second])continue;
vis[tmp.second] = 1;
for(int i = h[tmp.second]; ~i; i = nex[i]){
int j = to[i], val = w[i];
if(dis[j] > dis[tmp.second] + val){
dis[j] = dis[tmp.second] + val;
q.push({-dis[j], j});
}
}
}
if(dis[n] == 0x3f3f3f3f)return -1;
else return dis[n];
}
int main(){
cin>>n>>m;
int a, b, c;
memset(h, -1, sizeof h);
for(int i = 1; i <= m; i++){
cin>>a>>b>>c;
add(a, b, c);
}
cout<<Dijkstra()<<endl;
return 0;
}
bellman_ford算法
题目:有边数限制的最短路
题意:给你一个图,让你最多使用k条路,1~n之间的最短路,因为存在负权值,所以需要bellman_ford算法,只能用这个算法(最短路的几个算法)。时间复杂度O(nm)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
struct Node{
int u, v, w;
}edge[maxn];
int n, m, k, dis[maxn], vis[maxn], backup[maxn];
void bellman_ford(){
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for(int i = 1; i <= k; i++){
memcpy(backup, dis, sizeof(dis));//防止出现串联,就是刚更新完就用这个更新完的值去更新别的
for(int j = 1; j <= m; j++){
int val = edge[j].w;
dis[edge[j].v] = min(dis[edge[j].v], backup[edge[j].u] + val);
}
}
if(dis[n] > 0x3f3f3f3f/2)puts("impossible");//可能会出现这个路联通,但是使结果小于0x3f3f3f3f但是也是非常大的数(1, 5, inf),(5, n, -100),这么不会存在路,但是结果小于0x3f3f3f3f
else cout<<dis[n]<<endl;
}
int main(){
cin>>n>>m>>k;
for(int i = 1; i <= m; i++){
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
bellman_ford();
return 0;
}
spfa算法
题目:spfa求最短路
思路:本题存在负权值,所以比较建议使用spfa算法,bellman_ford算法应该会超时的。
时间复杂度O(m)最坏O(nm)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
to[tot] = b;
w[tot] = c;
nex[tot] = h[a];
h[a] = tot++;
}
int n, m, dis[maxn], vis[maxn];
int spfa(){
memset(dis, 0x3f, sizeof dis);
queue<int>q;
q.push(1);
dis[1] = 0;
vis[1] = 1;
while(q.size()){
int tmp = q.front();
q.pop();
vis[tmp] = 0;
for(int i = h[tmp]; ~i; i = nex[i]){
int j = to[i], val = w[i];
if(dis[j] > dis[tmp] + val){
dis[j] = dis[tmp] + val;
if(!vis[j]){
q.push(j);
vis[j] = 1;
}
}
}
}
if(dis[n] == 0x3f3f3f3f)return -1;
else return dis[n];
}
int main(){
cin>>n>>m;
int a, b, c;
memset(h, -1, sizeof h);
for(int i = 1; i <= m; i++){
cin>>a>>b>>c;
add(a, b, c);
}
int t = spfa();
if(t == -1)puts("impossible");
else cout<<t<<endl;
return 0;
}
spfa判断负环
题意:判断图中是否出现负环。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
to[tot] = b;
w[tot] = c;
nex[tot] = h[a];
h[a] = tot++;
}
int n, m, dis[maxn], vis[maxn],cnt[maxn];
int spfa(){
queue<int>q;
for(int i = 1; i <= n; i++){//所有的点为起点
vis[i] = 1;
q.push(i);
}
while(q.size()){
int tmp = q.front();
q.pop();
vis[tmp] = 0;
for(int i = h[tmp]; ~i; i = nex[i]){
int j = to[i], val = w[i];
if(dis[j] > dis[tmp] + val){
dis[j] = dis[tmp] + val;
cnt[j] = cnt[tmp] + 1;
if(cnt[j] >= n)return true;//存在负环
if(!vis[j]){
q.push(j);
vis[j] = 1;
}
}
}
}
return false;
}
int main(){
cin>>n>>m;
int a, b, c;
memset(h, -1, sizeof h);
for(int i = 1; i <= m; i++){
cin>>a>>b>>c;
add(a, b, c);
}
int t = spfa();
if(t == 0)puts("No");
else puts("Yes");
return 0;
}