注:本篇博客的思想來自於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;
}