算法总结
最短路笔记1:floyd
一、原理介绍
Floyd算法是最短路的一一种利用dp思想来求多点对多点的算法。基本的思想就是先枚举一个中间点,在枚举两个端点,判断两个端点中间,是否存在这两个端点分别到这个中间点的距离相加小于这两个点之间的当前最短路径,说明当前的路径是最短路径,更新就好了(感觉和区间dp有异曲同工之妙)。
二、使用步骤
1.状态转移方程
maps[i][j]=min(maps[i][j],maps[i][k]+maps[k][j]);
//k是枚举的中间点,i,j是两个端点
2.算法模板
void Floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
maps[i][j]=min(maps[i][j],maps[i][k]+maps[k][j]);
}
Floyd总结
该算法一般用的时进阶矩阵的存图方式,时间复杂度是O(n³)级别的,但是适用于多源对多目的地的情况。
最短路笔记2:dijkstra
一、算法原理
该算法用了贪心的原理,一个一个节点更新最小值,
主要有以下几步:
1.设置两个数组,bj用来标记节点是否去过,dis保存当前状态下该节点的最短路径。bj数组初始值为0,dis初始值为无限大。
2.令初始节点为dis为0,bj为1.
3.找到这些节点中dis最小的值(这里为方便描述,称该节点为a),然后开始更新,如果被更新节点的dis值大于节点a到此节点的距离加上dis[a]就使此节点的dis值等于节点a到此节点的距离加上dis[a],将与a相连的节点都进行此操作。
4.重复步骤3直到所有点更新完成
二、算法模板
1.朴素算法
void dij() {
memset(bj, 0, sizeof(bj));
dis[0]=0;
for (int i = 1; i <= n; i++) {
long long mini = inf, u;
for (int j = 1; j <= n; j++)
if (!bj[j] && mini > dis[j]) {
mini = dis[j];
u = j;
}
bj[u] = 1;
for (int j = 1; j <= n; j++)
dis[j] = min(dis[j], dis[u] + maps[u][j]);
}
for (int i = 1; i <= n; i++)
printf("%lld\n", dis[i]);
}
2.堆优化算法
void add(int u,int v)//链式前向星存图
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int dijkstra(int en)
{
memset(dis,inf,sizeof dis);
memset(bj,0,sizeof bj);
priority_queue<Node>q;
dis[1]=0;
q.push(Node(1,0));
while(!q.empty()){
int u=q.top().a;
q.pop();
if(bj[u])continue;
bj[u]=1;
for(int i=head[u];i;i=e[i].next){
int now=e[i].to;
if(dis[now]>dis[u]+1){
dis[now]=dis[u]+1;
path[now]=u;
q.push(Node(now,dis[now]));
}
}
}
if(dis[en]==inf)return -1;
else return dis[en]+1;
}
dijkstra总结
dij也是一种贪心算法,但是只能计算单源的,但是相比较floyd,dij效率高很多只要O(n²)的复杂度,堆优化后的dij更是O(2m + nlogn)时间复杂度,但dij也不能解决负权边,和负环。
最短路笔记3:SPFA
一、算法原理
SPFA一个对Ballman-Ford算法的bfs优化,求单源最短路径。
二、算法模板
void SPFA(int n)
{
for(int i=1;i<=n;i++)dis[i]=0x3f3f3f3f,bj[i]=0;
dis[1]=0;bj[1]=1;
queue<int>node;
node.push(1);
while(!node.empty())
{
int x=node.front();
node.pop();
bj[x]=0;
for(int i=head[x];i;i=side[i].next)
{
int y=side[i].to,w=side[i].w;
if(dis[y]>dis[x]+w)
{
dis[y]=dis[x]+w;
if(!bj[y])
{
bj[y]=1;
node.push(y);
}
}
}
}
}
SPFA算法总结
SPFA的对于稀疏图来说算法复杂度较低,但是对于稠密图来说,算法复杂度相当的高,并且SPFA可以判断负权边。
最短路笔记4:超级源点
全模板
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 500 + 10;
const int maxm = 100 + 10;
struct node
{
int id, dis;
node(int i, int j) : id(i), dis(j) {}
friend bool operator<(node a, node b)
{
return a.dis > b.dis;
}
};
struct edge
{
int to, w, next;
} e[maxm * maxn];
int n, m, st, en, cnt = 0;
int path[maxn];
int dis[maxn], bj[maxn];
int head[maxn];
int maps[maxn][maxn];
void add(int, int, int);
void printf_path(int); //输出路径
void dijkstra(int); //dijkstra朴素 O(n?)
void dijkstra_heap(int); //dijkstra堆优化 O(nlogn)
void floyd(); //O(n?)
void SPFA(int); //最好情况:O(m) 最差:O(nm)
int main()
{
//memset(maps, inf, sizeof maps);
scanf("%d%d", &m, &n);
for (int i = 1; i <= m; i++)
{
int u, v, w = 1;
scanf("%d%d%d", &u, &v, &w);
if (u == v)
continue;
//maps[u][v] = maps[v][u] = min(maps[u][v], w);
//add(u, v, w), add(v, u, w);
}
SPFA(1);
// printf_path(n);
// cout << endl;
if (dis[n] != inf)
cout << dis[n] << endl;
else
cout << "NO\n";
return 0;
}
void add(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
void dijkstra(int st)
{
memset(dis, 0x3f3f3f3f, sizeof dis);
memset(bj, 0, sizeof bj);
dis[st] = 0;
for (int i = 1; i <= n; i++)
{
int u, mini = inf;
for (int j = 1; j <= n; j++)
if (dis[j] < mini && !bj[j])
mini = dis[u = j];
bj[u] = 1;
for (int j = 1; j <= n; j++)
dis[j] = min(dis[j], dis[u] + maps[u][j]);
}
}
void dijkstra_heap(int st)
{
memset(dis, inf, sizeof dis);
memset(bj, 0, sizeof bj);
priority_queue<node> q;
dis[st] = 0;
q.push({st, 0});
while (!q.empty())
{
int u = q.top().id;
q.pop();
if (bj[u])
continue;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (dis[v] > dis[u] + e[i].w)
{
dis[v] = dis[u] + e[i].w;
q.push({v, dis[v]});
}
}
}
}
void SPFA(int st)
{
memset(bj, 0, sizeof bj);
memset(dis, inf, sizeof dis);
queue<int> q;
dis[st] = 0;
bj[st] = 1;
q.push(st);
while (!q.empty())
{
int u = q.front();
q.pop();
bj[u] = 0;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (dis[v] > dis[u] + e[i].w)
{
dis[v] = dis[u] + e[i].w;
path[v] = u;
if (!bj[v])
{
bj[v] = 1;
q.push(v);
}
}
}
}
}
void floyd()
{
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
maps[i][j] = min(maps[i][j], maps[i][k] + maps[k][j]);
}
void printf_path(int x)
{
if (path[x] == 0)
return;
printf_path(path[x]);
printf("%d ", x);
}