最短路
1.dijkstra
要求:所有边权为正
时间复杂度:(n+m)logn
int n, m;
int dist[N];
bool st[N];
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
2.spfa
时间复杂度:玄学复杂度,最慢可被卡到o(n*m)
int n, m;
int dist[N];
bool st[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])
{
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
最小生成树
1.prim算法
时间复杂度:o(n*n)
int n, m;
int g[N][N];
int dist[N];//记录未选的点到已选的集合中的点的最小距离
bool st[N];
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];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
2.Kruskal算法
时间复杂度:时间复杂度主要由排序方法决定,为o(e loge)
int n, m;
int fa[N];
struct Edge
{
int a, b, w;
bool operator< (const Edge &W)const
{
return w < W.w;
}
}edges[M];
int find(int x)
{
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
int kruskal()
{
sort(edges, edges + m);
for (int i = 1; i <= n; i ++ ) fa[i] = i;
int res = 0, cnt = 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)
{
fa[a] = b;
res += w;
cnt ++ ;
}
}
if (cnt < n - 1) return INF;
return res;
}
targan算法:
对于每个节点定义两个数组
1.dfn[u]:节点u搜索的次序编号(时间戳)
2.low[u]:为u或u的子树能够追溯到的最早的栈中节点的次序号
其余数组
stk[N]:栈
in_stk[N]:记录点是否在栈中
sze[N]:记录强连通分量中点的数目
id[N]:点属于哪一个强连通分量
scc_cnt:强连通分量的数量
int dfn[N],low[N],timestamp;
int stk[N],top;
int in_stk[N];
int sze[N],id[N],scc_cnt;
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u, in_stk[u] = true;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u])
{
++ scc_cnt;
int y;
do {
y = stk[top -- ];
in_stk[y] = false;
id[y] = scc_cnt;
sze[scc_cnt] ++ ;
} while (y != u);
}
}
最近公共祖先
1.倍增求公共祖先
int depth[N],fa[N][25];
void bfs(int root)
{
memset(depth,0x3f,sizeof depth);
depth[0]=0;//务必不能丢
depth[root]=1;
queue<int>que;
que.push(root);
while(que.size())
{
int t=que.front();
que.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(depth[j]>depth[t]+1)
{
depth[j]=depth[t]+1;
fa[j][0]=t;
for(int k=1;k<=20;++k)
fa[j][k]=fa[fa[j][k-1]][k-1];
que.push(j);
}
}
}
}
int lca(int a,int b)
{
if(depth[a]<depth[b])swap(a,b);
for(int i=20;i>=0;i--)
if(depth[fa[a][i]]>=depth[b])
a=fa[a][i];
if(a==b)return a;
for(int i=20;i>=0;i--)
{
if(fa[a][i]!=fa[b][i])
{
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}