首页 > 其他分享 >最小生成树/二分图

最小生成树/二分图

时间:2023-08-05 16:44:51浏览次数:32  
标签:二分 const idx int 最小 生成 return 匹配 节点

  • 最小生成树

    • 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;
        }
        
    • 最大流算法

标签:二分,const,idx,int,最小,生成,return,匹配,节点
From: https://www.cnblogs.com/moilip/p/17608172.html

相关文章

  • SQL-去除最大值与最小值求均值的问题
    背景今天有同事问我一道关于数据库SQL的面试题,我刚开始随便给了一个思路,后来思索发现这个思路有漏洞,于是总结下来,仅供参考。问题:薪水表中是员工薪水的基本信息,包括雇员编号,和薪水,查询除去最高、最低薪水后的平均薪水。一、薪水表信息CREATETABLE`salaries`(`emp_n......
  • 最大流 最小割
    网络流的一些定义和性质1.流网络(FlowNetwork)流网络为一个有向图\(G=(V,E)\),所有边\((u,v)\inE\)都有一个容量(Capacity),用\(c(u,v)\)表示。另外规定了两个点:源点(Source)、汇点(Sink),分别为\(s\)和\(t\quad(s\neqt)\)2.流定义流量为\(f(u,v)\)为\(V\timesV......
  • 论文解读:《利用生成性深度学习预测用于DNA编辑的设计者重组酶》》
    期刊:naturecommunications影响因子:16.6↓1.094中科院分区:1区摘要位点特异性酪氨酸型重组酶是基因组工程的有效工具,首个工程化变体已显示出治疗潜力。到目前为止,设计重组酶对新DNA靶位点选择性的适应主要是通过定向分子进化的迭代循环实现的。虽然有效,定向分子进化方法是费力和耗......
  • 8-5|生成一个纯色的ico图片
    fromPILimportImagedefcreate_solid_color_ico(color,size,file_path):  """  生成一个纯色的ICO图像。  参数:    color(tuple):RGB颜色值,例如(255,0,0)表示红色。    size(tuple):图像尺寸,例如(32,32)表示32x32的图像。 ......
  • Even(23Nowcoder6.J)(二分+可持久化线段树)
    题意:给定一个序列\(a\),定义一次操作选择序列中一个元素\(a[i]\),使\(a_i=\lfloor\frac{a_i}{2}\rfloor\),其中\(a_i\)为当前序列中的最大偶数,若没有则是最大奇数。有\(q\)组询问,每次给定\(k,l,r\)分别表示操作次数和操作区间,每次回答操作完成后区间中的\(Max\),询问间互相......
  • G4、CGAN|生成手势图像——可控制生成
    ......
  • OCR深度实践系列:数据生成
    转载:https://mp.weixin.qq.com/s?__biz=MzI5MjYzNzAyMw==&mid=2247484187&idx=1&sn=549b68ec989792ad5e2fb9179af55598&chksm=ec7f132bdb089a3d2f96ebecc780a6e756cdf26cb4e8a5bc4951c029e0c4dfb83c40cdc927ff&scene=21#wechat_redirect(一)图像预处理这篇为OCR深度实......
  • 二分图匹配概念&结论&证明的整理总结
    设\(M\)是\(G(V,E)\)的一个匹配先称\(M\)中的边为匹配边,不在\(M\)中的边为非匹配边与匹配边相关联的点,称之为配对点,不与匹配点相关联的点,称之为非配对点如果\(G\)中的每个点都是配对点,则称\(M\)是\(G\)的一个完美匹配在\(G\)中,由匹配边和非匹配边交替组成的......
  • go随机生成token
    const(defaultTokenLenint=16)funcGenerateToken()string{rand.Seed(time.Now().UnixNano())runes:=[]rune("abcdefghijklmnopqrstuvwxyz0123456789")b:=make([]rune,defaultTokenLen)fori:=rangeb{b[i]=r......
  • go使用jwt生成token
    常见的认证方式一般用户认证主流的方式大致上分为基于session和基于token这两种。基于sesion的认证方式用户向服务器发送用户名和密码。服务器验证通过后,在当前对话(sesion)里面保存相关数据,比如用户角色、登录时间等等。服务器向用户返回一个session_id,写入用户的Co......