【董晓算法】竞赛常用知识之图论2(最小环,最小生成树)

   前言:

本系列是学习了董晓老师所讲的知识点做的笔记

董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com)

 动态规划系列(还没学完)

【董晓算法】动态规划之线性DP问题-CSDN博客

【董晓算法】动态规划之背包DP问题(2024.5.11)-CSDN博客

【董晓算法】动态规划之背包DP与树形DP-CSDN博客

字符串系列()

【董晓算法】竞赛常用知识之字符串1-CSDN博客

【董晓算法】竞赛常用知识之字符串2-CSDN博客

数据结构系列(未学完)

【董晓算法】竞赛常用知识点之数据结构1-CSDN博客

搜索系列

[董晓算法]搜索相关题目及模板-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)优选答案即可。

  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步操作,直到圈内为空。

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条边为

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;
}

对比

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值