最短路问题
图论:最短路问题
一、单源最短路问题--所有边均为正权边--dijkstra算法
dijkstra算法基于贪心,思路本质上就是从节点1开始,一遍一遍找距离节点1最近的点,找到最近的点之后标记并用该点更新其所相邻的点与1的距离,
之后再从未标记的点中找到距离最短的点(实际上只是找标记点的临点)//堆优化也是优化的这个地方
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 10010,M = 20020 INF = 0x3f3f3f;
int m, n;
int h[N], e[M], w[M], ne[M], idx;
int g[N][N];
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 朴素版dijkstra算法(稠密图)--邻接矩阵:O(n^2+m)
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; 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] == INF)
return -1;
else
return dist[n];
}
// 堆优化版dijkstra算法(稀疏图)--邻接表O(mlogn)
int dijkstra()
{
memset(dist, 0x3f3f3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap; // 小根堆就是这样写,往里push的时候只看第一个就可以了,如果写成priority_queue<PII>heap;
// 就是默认大根堆了
heap.push({0, 1});
while (heap.size())
{
auto t = heap.top();
int ver = t.second, distance = t.first;
if (st[ver])
continue;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > w[i] + distance)
{
dist[j] = w[i] + distance;
heap.push({dist[i], j});
}
}
st[ver] = true;
}
if (dist[n] == 0x3f3f3f)
return -1;
return dist[n];
}
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
g[i][j] == 0;
else
g[i][j] = INF;
}
}
//邻接矩阵
while (m--)
{
int a, b, w;
cin >> a >> b >> w;
g[a][b] = min(g[a][b], w); // 因为a与b之间可能有多条边,最短路就取得最小值就好了
}
//邻接表
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c); //邻接表存储有重边也无所谓了
}
int t = dijkstra();
cout << t << endl;
return 0;
}
二、单源最短路--存在负权边--Bellman-Ford算法和SPFA算法
Bellman_ford算法思路:对所有点每次对所有边全部遍历一遍,每条边都松弛n次,就会得到最短路。
spfa算法思路:是Bellman_ford算法利用队列优化,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 10010,M = 20010, INF = 0x3f3f3f;
int m, n, k;
int h[N], e[M], w[M], ne[M], idx;
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int dist[N], bakcup[N];
struct
{
int a, b;
int w;
} edge[N];
int Bellman_ford() // 有边数限制
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < k; i++)
{
memcpy(dist, bakcup, sizeof dist);
for (int j = 1; j <= m; j++)
{
int a = edge[j].a, b = edge[j].b, w = edge[j].w;
if (dist[b] > bakcup[a] + w)
dist[b] = bakcup[a] + w;
}
}
if (dist[n] > INF / 2)
return -1;
return dist[n];
}
// 无边数限制
int Bellman_ford()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int a = edge[j].a, b = edge[j].b, w = edge[j].w;
if (dist[b] > dist[a] + w)
dist[b] = dist[a] + w;
}
}
if (dist[n] > INF / 2)
return -1;
return dist[n];
}
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
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]){
st[j]=true;
q.push(j);
}
}
}
}
if(dist[n]==INF) return -1;
return dist[n];
}
//spfa判断是否存在负环
int cnt[N];
bool spfa_c(){
queue<int>q;//因为这是有向图,所以仅从1这个点并不能完全判断图中是否存负环
for(int i=1;i<=n;i++){
q.push(i);
st[i]=true;//只要进队就标记,出对一样
}
while(q.size()){
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];
cnt[j]=cnt[t]+1;//这里就和dfs中记录字数个数一样,cnt用来记录1到j点的个数(不包含自己),如果点数超过n
//由抽屉定理就可以判断有负环了
if(cnt[j]>=n) return true;
if(!st[j]){
st[j]=true;
q.push(j);
}
}
}
}
return false;
}
int main()
{
cin >> m >> n;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
edge[i].a = a, edge[i].b = b, edge[i].w = c;
}
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
return 0;
}
三、多源最短路--floyd算法
基于动态规划实现
#include<bits/stdc++.h>
using namespace std;
const int N = 100010,INF=0x3f3f3f;
int n,m,Q;
int d[N][N];
//用于计算d[a][b],表示a到b的距离,将所有距离全部算好
int floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
}
}
}
int main(){
cin>>m>>n>>Q;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j) d[i][j]=0;
else d[i][j]=INF;
}
while(m--){
int a,b,c;
cin>>a>>b>>c;
d[a][b]=min(d[a][b],c);
}
floyd();
while(Q--){
int x,y;//求x到y的最短距离,源点不唯一,所以用floyd算法
cin>>x>>y;
if(d[x][y]>INF/2) cout<<"impossible"<<endl;//因为可能存在负权边,所以当x和y之间不存在通路时,d[x][y]不一定是正无穷
else cout<<d[x][y]<<endl;
}
return 0;
}
posted on 2024-10-23 14:18 Minyou0713 阅读(16) 评论(0) 编辑 收藏 举报