朴素版Dijkstra算法
朴素版Dijkstra算法用于解决稠密无负权边图的最短路问题。
int n, m; //n(点数) m(变数)
int dis[N], g[N][N]; //dis[x](起点1到x的距离) g[a][b](a, b点间的距离)
bool v[N]; //标记数组
int dijkstra()
{
memset(dis, 0x3f, sizeof dis); //初始化所有点到起点1的距离为正无穷
dis[1] = 0; //把1的距离赋为0;
for(int i = 0; i < n - 1; i ++ ) //循环( n - 1 )次
{
int t = -1;
for(int j = 1; j <= n; j ++ )
if(!v[j] && (t == -1 || dis[j] < dis[t])) //找出未被标记的点中距离起点最小的点
t = j;
v[t] = true; //标记点t
for(int j = 1; j <= n; j ++ ) //用起点到t点的最短距离更新其他点
dis[j] = min(dis[j], dis[t] + g[t][j]);
}
if(dis[n] == 0x3f3f3f3f) return -1; //如果图不联通就返回-1
return dis[n];
}
堆优化Dijkstra算法
堆优化Dijkstra算法用于解决稀疏无负权边图的最短路问题。
int n, m, dis[N]; //n(点数) m(边数) dis[x](起点到x的距离)
bool v[N]; // v[N](标记数组)
vector<PII> g[N]; //邻接表存图
int dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII>> q; //优先队列实现小根堆
q.push({0, 1}); //把起点存入堆
//(需要找出的是距离起点最小的点,应该把距离放在第一位(先按第一位排序))
memset(dis, 0x3f, sizeof dis); //初始化每个点到起点的距离
dis[1] = 0; //起点距离初始化为0
while(q.size())
{
int t = q.top().second; //当前遍历到点t
q.pop(); //把点从堆里弹出
if(v[t]) continue; //如果点t点被遍历到了就跳过该点
v[t] = true; //标记点t
for(int i = 0; i < g[t].size(); i ++ )//遍历所有与点t有边相连的点
{
int j = g[t][i].first, w = g[t][i].second;
//j(与点t有边相连的点) w(边a-b的权值)
if(dis[j] > dis[t] + w) //点j需要更新
{
dis[j] = dis[t] + w;
q.push({dis[j], j});
//点j更新后会影响所有与j有边相连接的点,需要把点j存入堆
}
}
}
if(dis[n] == 0x3f3f3f3f) return -1; //图不联通返回-1
return dis[n];
}
Bellman_ford
bellman_ford算法用于解决有边数限制的带负权边图的最小路问题
struct node
{
int l, r, w; //l(边的左顶点) r(边的右顶点) w(边权值)
}e[M];
int n, m, k; // n(点数) m(边数) k(限制边数)
int dis[N], last[N]; //dis[x](起点到x的距离) last[x](上次循环的出的起点到x的距离)
void bellman_ford()
{
memset(dis, 0x3f, sizeof dis); //初始化起点到每个点的距离
dis[1] = 0;
for(int i = 0; i < k; i ++ )//限制变数k, 遍历k次
{
memcpy(last, dis, sizeof dis); //把上次循环得出的dis赋给last
for(int j = 0; j < m; j ++ ) //循环每条边(a-b)
{
int a = e[j].l, b = e[j].r, w = e[j].w;
dis[b] = min(dis[b], last[a] + w);
//dis[b]用上次循环得出的起点到a点的距离加上边a-b的权值
}
}
}
SPFA算法
spfa算法用来求无边数限制带负权边图的最短路,还可以用来判断一个图是否存在负环。
spfa算法求最短路
int spfa()
{
queue<int> q; //创建一个队列q来存储更新距离了的点
q.push(1);
memset(dis, 0x3f, sizeof dis); //初始化起点到每个点的距离
dis[1] = 0;
while(q.size())
{
int t = q.front();
q.pop(); v[t] = false; //将点t弹出队列并且取消点t的标记
for(int i = 0; i < g[t].size(); i ++ ) //遍历与点t有相连接边的所有点
{
int j = g[t][i].first, w = g[t][i].second;
//j(与t有连接边的点) w(边的权值)
if(dis[j] > dis[t] + w) //如果dis[j]需要更新
{
dis[j] = dis[t] + w;
if(!v[j]) //点j不在队列中时把j点存入队列
{
v[j] = true;
q.push(j);
}
}
}
}
return dis[n];
}
spafa判断负环
int n, m; //n(点数) m(边数)
int f[N], dis[N], cnt[N]; //f[](标记数组) cnt[x](起点到点x的最短路径中的边数)
vector<PII> g[N];
int spfa()
{
queue<int> q;
for(int i = 1; i <= n; i ++ ) //判断负环而不是判断从一开始的负环所以负环可能从一到不了
{
q.push(i); f[i] = true; //把所有点放入初始点集
}
while(q.size())
{
int t = q.front();
q.pop(); f[t] = false;
for(int i = 0; i < g[t].size(); i ++ )
{
int j = g[t][i].first, w = g[t][i].second;
if(dis[j] > dis[t] + w)
{
dis[j] = dis[t] + w;
cnt[j] = cnt[t] + 1;
if(cnt[j] >= n) return true; //判断得出图里含负环
//由抽屉原理(n条边最少经过了n+1个点,图里只有n个点所以图中一定存在负环)
if(!f[j])
{
q.push(j);
f[j] = true;
}
}
}
}
return false;
}