[图论第三节]最小生成树/二分图

这篇具有很好参考价值的文章主要介绍了[图论第三节]最小生成树/二分图。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  • 最小生成树

    • Prim算法

      • 朴素版Prim O(n^2)
        • 稠密图
        • 步骤:
          • S:表示最小生成树的集合
          • 初始化距离
          • 找距离集合S最近的节点
          • 将节点加入集合S
          • 用该节点更新非S点到集合S点的距离
        • 代码:
          const int N = 510;
          const int INF = 0x3f3f3f3f;
          int g[N][N];
          int d[N];//非生成树节点与生成树节点的最短距离
          int st[N];
          int n, m;
          
          int prim(){
              memset(d, 0x3f, sizeof d);//初始化
              
              int res = 0;//记录生成树权值和
              
              for(int i = 0; i < n; ++ i){//遍历n次选取n个点加入生成树
                  
                  int t = 0;
                  for(int j = 1; j <= n; ++ j){
                      if(!st[j] && (!t || d[j] < d[t]))//在非生成树集合中选取最小距离的点
                          t = j;
                  }
                  st[t] = 1;//加入生成树集合
                  
                  if(i && d[t] == INF) return INF;//某个点与生成树集合不联通,则不存在生成树
                  if(i) res += d[t]; //累加最小权值
                  
                  for(int j = 1; j <= n; ++ j) //更新其他点到生成树的距离
                      d[j] = min(d[j], g[t][j]);
              }
              
              return res;
              
          }
          int main(){
              cin >> n >> m;
              
              memset(g, 0x3f, sizeof g);
              
              while(m -- ){
                  int u, v, w;
                  cin >> u >> v >> w;
                  g[u][v] = g[v][u] = min(g[u][v], w);//无向图去重边
              }
              
              int t = prim();
              if(t == INF) cout << "impossible";
              else cout << t;
              return 0;
          }
          
      • 堆优化版Prim O(mlogn)

        • 用堆优化找最短距离的过程将O(n)-->O(1)
    • Kruskal算法 O(mlogm)

      • 稀疏图
      • 步骤:
        • 将所有边的权值从小到大排序
        • 枚举每条边u--v, w
        • 若u,v不同时在生成树集合中,那么将这条边加入集合
        • 利用并查集
      • 代码:
        const int N = 2e5 + 10;
        const int INF = 0x3f3f3f3f;
        int f[N];
        struct edge{
            int u, v, w;
            //运算符重载
            bool operator< (const edge &e)const{
                return w < e.w;
            }
        }edges[N];
        
        int n, m;
        
        int find(int x){
            return x == f[x] ? x : (f[x] = find(f[x]));
        }
        
        int kruskal(){
            sort(edges + 1, edges + 1 + m);//权值排序
            
            for(int i = 1; i <= n; ++ i) f[i] = i;//并查集初始化
            
            int res = 0, cnt = 0;//res记录总权值,cnt记录边的条数
            for(int i = 1; i <= m; ++ i){//遍历每条边
                int u = edges[i].u;//获取信息
                int v = edges[i].v;
                int w = edges[i].w;
                u = find(u), v = find(v);//祖宗节点
                if(u != v){//不在同一集合中
                    res += w;
                    cnt ++ ;
                    f[u] = v;//加入同一集合
                }
            }
            
            if(cnt < n - 1) return INF;//边数小于节点数-1,则不连通,不存在最小生成树
            else return res;
        }
        int main(){
            cin >> n >> m;
            
            for(int i = 1; i <= m; ++ i){
                int a, b, c;
                cin >> a >> b >> c;
                edges[i] = {a, b, c};
            }
            
            int t = kruskal();
            if(t == INF) cout << "impossible";
            else cout << t;
            return 0;
        }
        
  • 二分图

    • 判别二分图-染色法 O(m + n)

      • 一个图是二分图当接近当图中不含有奇数(就是离散数学中的二部图)
      • 代码:
        const int N = 1e5 + 10;
        const int M = 2 * N;
        int h[N], e[M], ne[M], idx = 1;
        int color[N];
        int n, m;
        
        void add(int u, int v){
            e[idx] = v;
            ne[idx] = h[u];
            h[u] = idx;
            idx ++ ;
        }
        
        //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;//染色出现矛盾
                }else if(color[j] == c) return false;//已经染色,判断相邻点是否同色
            }
            return true;
        }
        
        int main(){
            cin >> n >> m;
            while(m -- ){
                int u, v;
                cin >> u >> v;
                add(u, v);//无向图建边
                add(v, u);
            }
            
            bool flag = true;
            for(int i = 1; i <= n; ++ i){//以所有点为起点染色,因为可能不连通
                if(!color[i]){
                    flag = dfs(i, 1);
                    if(!flag) break;
                }
            }
            
            if(flag) cout << "Yes";
            else cout << "No";
            return 0;
        }
        
    • 匈牙利算法 最坏O(mn)

      • 求二分图的最大匹配数
      • 代码:
        const int N = 510;
        const int M = 1e5 + 10;
        int h[N], e[M], ne[M], idx = 1;
        int match[N]; 
        int st[N];
        int n1, n2, m;
        
        void add(int a, int b){
            e[idx] = b;
            ne[idx] = h[a];
            h[a] = idx ++ ;
        }
        
        bool find(int x){
            for(int i = h[x]; i; i = ne[i]){ //遍历该节点邻接的所有节点
                int j = e[i];
                if(!st[j]){
                    st[j] = 1;//不管j有没有匹配过,强制标记为已经匹配,为了防止匹配j的节点再次将j匹配
                    if(!match[j] || find(match[j])){ //j没有匹配或者匹配j的节点可以换一个匹配,在换匹配的过程中不会再匹配j,因为j已经被标记
                        match[j] = x; //将x与j匹配
                        return true; //成功匹配
                    }
                }
            }
            return false;
        }
        
        int main(){
            cin >> n1 >> n2 >> m;
            
            while(m -- ){
                int a, b;
                cin >> a >> b;
                add(a, b); //只用一次
            }
            
            int res = 0;//记录最大匹配数
            for(int i = 1; i <= n1; ++ i){//匹配每一个节点
                memset(st, 0, sizeof st); 
                if(find(i)) res ++ ; //匹配成功
            }
            
            cout << res;
            return 0;
        }
        
    • 最大流算法

文章来源地址https://www.toymoban.com/news/detail-634767.html

到了这里,关于[图论第三节]最小生成树/二分图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法提高-图论- 最小生成树

    和上题一摸一样,但是题意要求的out看起来需要特别处理,其实只要想清楚kruskal的性质就好 题意就是求最小生成树,因为边权都是正数,那么我们多选一条边必然会导致我们的代价增大 但如果题目说了边权为正或者负数,那么就不是最小生成树了,极端一点所有边都是负的

    2024年02月16日
    浏览(43)
  • 第三章 图论 No.5最小生成树之虚拟源点,完全图与次小生成树

    虚拟源点:1146. 新的开始 1146. 新的开始 - AcWing题库 与一般的最小生成树问题不同,本题需要在建立电站的电井之间建立电网,在两个电站之间建立电网需要花费金额,可以看成一条具有权值的边 但是建立电网的前提是:其中一个电井需要建立电站,建立电站也需要费用 已经

    2024年02月14日
    浏览(40)
  • 图论13-最小生成树-Kruskal算法+Prim算法

    基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中 不产生 回路,直至森林变成一棵树为止。 2.2.1 如果图不联通,直接返回空,该

    2024年02月01日
    浏览(46)
  • 图论与算法(遍历、最小生成树、最短路径)

    图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E),其中:顶点集合V = {x|x属于某个数据对象集}是有穷非空集合;E = {(x,y)|x,y属于V}或者E = {x, y|x,y属于V Path(x, y)}是顶点间关系的有穷集合,也叫做边的集合。(x, y)表示x到y的一条双向通路,即(x, y)是无方向的;Path

    2024年04月14日
    浏览(45)
  • acwing算法提高之图论--最小生成树的典型应用

    本专题用来记录使用prim算法或kruskal算法求解的题目。 题目1 :1140最短网络 C++代码如下, 题目2 :1141局域网 C++代码如下, 题目3 :1142繁忙的都市 C++代码如下, 题目4 :1143联络员 C++代码如下, 题目5 :1144连接格点 C++代码如下,

    2024年04月27日
    浏览(40)
  • acwing算法提高之图论--最小生成树的扩展应用

    本专题用来记录使用最小生成树算法(prim或kruskal)解决的扩展题目。 题目1 :1146新的开始 C++代码如下, 题目2 :1145北极通讯网络 C++代码如下, 题目3 :346走廊泼水节 C++代码如下, 题目4 :1148秘密的牛奶运输 C++代码如下,

    2024年04月16日
    浏览(37)
  • 【算法每日一练]-图论(保姆级教程篇7 最小生成树 ,并查集模板篇)#村村通 #最小生成树

    目录 题目:村村通 并查集  题目:最小生成树 kruskal算法 prim算法          先引入问题: 要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的 任意两个之间都可以通信 ,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要 使铺设光

    2024年02月04日
    浏览(77)
  • 【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)

    最小生成树 有关树的定义 生成子图 :生成子图是从原图中选取部分节点以及这些节点之间的边所组成的图。生成子图中的所有节点和边都必须在原图中存在。 生成树 :一个连通 无向图 的 生成子图 ,同时要求是树。也即在图的边集中选择 n - 1 条,将所有顶点连通。 我们

    2024年02月16日
    浏览(40)
  • 【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(45)
  • 【算法导论】图论(图的基本概念,图上的深度优先搜索(DFS),广度优先搜索(BFS),最小生成树(MST)及Prim,Kruskal算法)

    图(Graph)是一种包含节点与节点的边的集合,记作G=(V,E),V是节点的集合,E是边的集合。 有向图 一个有向图G=(V,E),E中每个元素是V上的一个二值关系:一条从a出发的连向b的边e可以记作一个 有序 对e = (a,b) 。 无向图 一个无向图G=(V,E),E的每个元素e可以表示V上的一个 无序 对,记

    2024年02月03日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包