1.朴素版的dijkstra()算法(适用于稠密图)O(n^2):
首先把dist都初始化为0x3f3f3f3f,然后1号点dist置0,然后进行n - 1次循环更新剩余的n - 1个点到1号点距离。
循环内容:首先循环找到目前为止dist最小的而且不在集合当中的一个点t,然后用该点更新所有它可以到达的点。最后将点t加入集合。
最后如果n号点的dist距离仍为inf,则到达不了,否则输出dist[n]。邻接矩阵存放图。
2.堆优化版的dijkstra()算法(适用于稀疏图)O(mlogn):
我们可以把距离与对应点的信息放到小根堆里面,优化朴素版本寻找min dist的步骤,然后借助队列更新dist数组。邻接表存放图。
定义小根堆: priority<PII,vector
3.bellman_ford()算法(求限制步数的最短路问题) O(nm):
如果步数限制为k,那么就循环k次,然后循环所有边,更新dist数组:dist[b] = min(dist[b], backup[a] + w);记得要用备份数组backup储存上一步的dist,以免发生串联
4.SPFA算法 一般O(m), 最坏O(nm):
对bellman_ford算法做出的优化算法,用队列存放dist更新了的点,宽搜优化。用st数组存放点是否在队列中的状态,当dist更新的话,如果!st[i],则更新st[i] = true;
SPFA求负环:我们把所有点加入队列,然后通过队列维护cnt数组,如果cnt[i] >= n说明存在负环。
5.floyd()算法 (多源汇最短路算法)O(n^3):
首先初始化d[] [],然后三重循环k,i,j,d[i] [j] = min(d[i] [j], d[i] [k] + d[k] [j]);
6.prim()算法 O(n^2):
初始化dist数组为0x3f3f3f3f,然后迭代n次,每次找出不在集合中而且距离集合最短的点,将他加入集合,并用它更新其他点的距离dist。
//模板
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
st[t] = true;
}
return res;
}
7.kruskal()算法 O(mlogm):
按边权重从小到大排序每条边(结构体存储,重载一个小于号),然后从小到大枚举,如果a,b不连通,那么就将他们连起来(并查集)。
//存放边的结构体
struct node
{
int a, b, w;
bool operator< (const node & W)const
{
return w < W.w;
}
}edges[M];
//并查集模板(加路径压缩)
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
//首先初始化并查集
for (int i = 1; i <= n; i ++ ) p[i] = i;
//cnt存放已加入的边的数量,ans为最小生成树权重之和
int cnt = 0, ans = 0;
for (int i = 0; i < m; i ++ )
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b)
{
p[a] = b;
cnt ++ ;
ans += w;
}
}
8.染色法判断二分图 O(n + m):
首先for循环迭代每个点,如果这个点还没有染色,那么就将这个点染色,然后dfs它的所有临点,并且将没有染过色的临点染色成另外一种颜色,如果临点已经染过色了那么判断相邻两点颜色是否一致,一致的话返回false。
//part
bool flag = true;
for (int i = 1; i <= n; i ++ )
{
if (!color[i])
{
if (!dfs(i, 1))
{
flag = false;
break;
}
}
}
//dfs
bool dfs(int u, int c)
{
color[u] = c;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!color[j])
{
if (!dfs(j, 3 - c)) return false;
}
if (color[j] == c) return false;
}
return true;
}
9.匈牙利算法 O(mn)实际运行效果很好:
首先邻接表存左边到右边的一条边,因为不需要反向访问,所以不用存右边到左边的边。然后st数组存放每个右边的点在本次是否被访问过(如果没有st数组,假如碰到环了会死循环),match数组存放和右边匹配的左边的点。接着循环左边所有点,如果find(i)可以匹配到,那么答案++。find函数:访问x的所有临边j,如果st[j] == 0即未被访问,那么就访问它,然后看j是否有match,如果有的话看一下是否可以把j的match换一个匹配即find(match[j]),如果可以就换,然后把x与j匹配起来。
匈牙利算法部分重要代码:
bool find(int x)
{
for (int i = h[x]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true;
if (!match[j] || find(match[j]))
{
match[j] = x;
return true;
}
}
}
return false;
}