最短路算法
最短路算法:
单源最短路:所有边权都是正数
1、Dijkstra算法:
对于稠密图(边相对较多):朴素Dijkstra 时间复杂度:\(O(n^2+m)\)
思路分析:该算法是基于贪心思想的,每次找到最小的没有被用来更新其他点的dist[t],再用它来更新它的出边,并在st中打上标记即可
849. Dijkstra求最短路 I - AcWing题库
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int N = 510;
int g[N][N];
int dist[N];
int n, m;
bool st[N];
int dijkstra()
{
dist[1] = 0;
for(int i = 1; i <= n; i ++)
{
int t = -1;
for(int j = 1; j <= n; j ++)
{
if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
}
st[t] = true;
for(int j = 1; j <= n; j ++) dist[j] = min(dist[j], dist[t] + g[t][j]);
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main ()
{
//IOS;
memset(dist, 0x3f, sizeof dist);
memset(g, 0x3f, sizeof g);
cin >> n >> m;
For(i, 1, m)
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = min(g[a][b], c);
}
cout << dijkstra() << endl;
return 0;
}
/*
*/
对于稀疏图(边相对较少):堆优化Dijkstra 时间复杂度:\(O(mlogn)\)
思路分析:相对于朴素Dijkstra,通过堆,我们每次可以用\(O(1)\)的时间去查找dist[t],然后再用其去更新t的出边,但更新这一步维护堆的过程每次时间复杂度为\(O(logn)\),一共m次,因此时间复杂度\(O(mlogn)\)
850. Dijkstra求最短路 II - AcWing题库
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int N = 150050;
typedef pair<int , int> PII;
int n, m;
int h[N], e[N], w[N], nxt[N], idx;
bool st[N]; //
int dist[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx ++;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
priority_queue <PII, vector<PII>, greater<PII> > heap;
dist[1] = 0;
heap.push({0, 1});
while(!heap.empty())
{
auto t = heap.top();
heap.pop();
int ver = t.se, distance = t.fi;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = nxt[i])
{
int j = e[i];
if(dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main ()
{
//IOS;
cin >> n >> m;
memset(h, -1, sizeof h);
For(i, 1, m)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
cout << dijkstra() << endl;
return 0;
}
/*
*/
单源最短路:存在负权边
3、Bellman-Ford算法:可以处理最多经过k条边的问题
算法分析:外层循环k次,内层遍历所有的边并进行松弛操作,即令所有的边的满足三角不等式dist[b] <= dist[a] + w[i],而外层循环的意义就是,相当于一层一层的去松弛求最短路,外层遍历几次即最短路径过几条边,需要注意的问题是每次松弛必须用上一层的dist,以防止串联
同时可以利用如果外层循环超过n次,仍能进行松弛更新,即存在负环
时间复杂度:\(O(nm)\)
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int M = 1e5 + 5;
const int N = 505;
int n, k, m;
int dist[N];
int backup[N];
struct node
{
int a, b, w;
}edge[M];
int bmf()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for(int i = 1; i <= k; i ++)
{
memcpy(backup, dist, sizeof dist);//备份上一层的dist
for(int j = 1; j <= m; j ++)
{
int a = edge[j].a, b = edge[j].b, w = edge[j].w;
if(dist[b] > backup[a] + w) dist[b] = backup[a] + w;
}
}
return dist[n];
}
int main ()
{
//IOS;
cin >> n >> m >> k;
For(i, 1, m)
{
int a, b, w;
cin >> a >> b >> w;
edge[i] = {a, b, w};
}
int t = bmf();
if(t >= 0x3f3f3f3f / 2) cout << "impossible" << endl;
else cout << t << endl;
return 0;
}
/*
*/
4、SPFA算法:队列优化的Bellman-Ford算法
算法分析:我们知道Bellman-Ford算法在内层循环的时候每次会遍历所有的边,但实际上对于t,我们只需要遍历t的所有临边即可,因此我们可以利用队列,同时通过st状态数组防止队列中点的重复,每次将队头的临边更新即可。
时间复杂度:平均\(O(m)\),最坏情况\(O(nm)\),因此很容易被出题人卡
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int N = 1e5 + 5;
int e[N], h[N], ne[N], idx, w[N];
bool st[N];
int dist[N];
int n, m;
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
int spfa()
{
queue <int> q;
memset(dist, 0x3f3f3f3f, sizeof dist);
dist[1] = 0;
q.push(1);
st[1] = true;
while(!q.empty())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if(!st[j])
{
q.push(j); //此时推进到j,dist[j]一定是最短路
st[j] = true;
}
}
}
}
return dist[n];
}
int main ()
{
//IOS;
memset(h, -1, sizeof h);
cin >> n >> m;
For(i, 1, m)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
int t = spfa();
if(t == 0x3f3f3f3f) cout << "impossible" << endl;
else cout << t << endl;
return 0;
}
/*
*/
SPFA判断负权回路
算法分析:我们可以开一个数组cnt来记录每个点最短路的经过的边数,一张有n个点图中,最短路不可能超过n-1,倘若cnt[i] >= n 就说明一定存在负权回路
PS:需要注意的是,最开始初始化队列的时候需要把每个点都放进队列,因为此时不是求1-n的最短路,每个点都有无法被遍历到的可能性。
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int N = 2005;
const int M = 10005;
int e[M], h[N], w[M], idx, ne[M];
bool st[N];
int cnt[N];
int n, m;
int dist[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool spfa()
{
memset(dist, 0x3f, sizeof dist);
queue <int> q;
for(int i = 1; i <= n; i ++)
{
q.push(i);
st[i] = true;
}
while(!q.empty())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
cnt[j] = cnt[t] + 1;
if(cnt[j] >= n) return true;
dist[j] = dist[t] + w[i];
if(!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main ()
{
//IOS;
cin >> n >> m;
memset(h, -1, sizeof h);
For(i, 1, m)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
if(spfa()) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
/*
*/
多源汇最短路
1、Floyd算法:(不可求负权边)
算法分析:Floyd是基于动态规划思想的算法,转移方程为\(d[k][i][j] = min(d[k][i][j], d[k-1][i][k] + d[k-1][k][j])\)
省去第一维后,即\(d[i][j] = min(d[i][j], d[i][k] + d[k][j])\)
时间复杂度:\(O(n^3)\)
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 1e9;
const int MOD = 1000000007;
const ll MOD1 = 212370440130137957ll;
const int N = 205;
int n, m, t;
int d[N][N];
void floyd()
{
For(k, 1, n)
For(i, 1, n)
For(j, 1, n)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main ()
{
IOS;
cin >> n >> m >> t;
For(i, 1, n)
For(j, 1, n)
if(i == j) d[i][j] = 0;
else d[i][j] = MAXN;
For(i, 1, m)
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = min(d[a][b], c);
}
floyd();
For(i, 1, t)
{
int a, b;
cin >> a >> b;
if(d[a][b] > MAXN / 2) cout << "impossible" << endl;
else cout << d[a][b] << endl;
}
return 0;
}
/*
*/


浙公网安备 33010602011771号