前言:
本系列是学习了董晓老师所讲的知识点做的笔记
董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com)
动态规划系列(还没学完)
【董晓算法】动态规划之背包DP问题(2024.5.11)-CSDN博客
字符串系列()
数据结构系列(未学完)
搜索系列
图论系列
【董晓算法】算法知识之图论1(拓扑排序,多种最短路算法)-CSDN博客
无向图的最小环问题
Floyd 算法求最小环
1.Floyd 算法有一个性质:在最外层循环到点k时**(尚未开始k次循环)**dij表示的是从i到j且仅经过编号1~k-第k次循环)的点的最短路(即途经编号之k点的最短路尚未计算)
2.设最小环中编号最大的顶点为k,环上与k相邻的两个点为i,j,则在最外层循环枚举到k时,该环的长度为ans=dij+wik+wki。
3.故在循环时对于每个k枚举满足i<k且j<k 的点对(i,j)优选答案即可。
cpp
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j) w[i][j]=1e8;
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
w[a][b]=w[b][a]=c;
}
memcpy(d,w,sizeof d);
for(int k=1; k<=n; k++){
for(int i=1; i<k; i++)
for(int j=i+1; j<k; j++)
ans=min(ans,d[i][j]+w[j][k]+w[k][i]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
最小生成树
Prim 算法
Prim(普里姆)算法基于贪心思想的最小生成树(MST) 算法
e[u]存u点的所有邻边的终点和边权。
d[u]存u点与圈外邻点的最小距离,vis[u] 标记u点是否出圈。
算法流程类似 Dikstra 算法,不断选距离最小的点出圈,直到圈内为空
1.初始化,所有点都在圈(集合)内vis=0,d[s]=0,d[其它点]=+无穷。
2.每次从圈内选取一个距离最小的点 u,打标记移出圈。
3.对u的所有邻点的距离执行更新操作
4.重复2.3步操作,直到圈内为空。
cpp
struct edge{int v,w;};
vector<edge> e[N];
int d[N], vis[N];
bool prim(int s){
for(int i=0;i<=n;i++)d[i]=inf;
d[s]=0;
for(int i=1;i<=n;i++){
int u=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&d[j]<d[u]) u=j;
vis[u]=1;
ans+=d[u];
if(d[u]!=inf) cnt++;
for(auto ed : e[u]){
int v=ed.v, w=ed.w;
if(d[v]>w) d[v]=w;
}
}
return cnt==n;
}
int main(){
cin>>n>>m;
for(int i=0; i<m; i++){
cin>>a>>b>>c;
e[a].push_back({b,c});
e[b].push_back({a,c});
}
Kruskal(克鲁斯卡尔)算法
Kruskal(克鲁斯卡尔)算法利用并查集求最小生成树(MST)
e[i] 存第i条边的起点、终点与边权
fa[x] 存x点的父节点。
1.初始化并查集,把n个点放在几个独立的集合
2.将所有的边按边权从小到大排序(贪心思想)。
3.按顺序枚举每一条边,如果这条边连接的两个点不在同一集合就把这条边加入最小生成树,并且合并这两个集合;如果这条边连接的两个点在同一集合一就跳过。
4.重复执行3,直到选取了 n-1条边为止
cpp
truct edge{
int u,v,w;
bool operator<(const edge &t)const
{return w < t.w;}
}e[N];
int fa[N],ans,cnt;
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
bool kruskal(){
sort(e,e+m);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=0; i<m; i++){
int x=find(e[i].u);
int y=find(e[i].v);
if(x!=y){
fa[x]=y;
ans+=e[i].w;
cnt++;
}
}
return cnt==n-1;
}