首页 > 其他分享 >次小生成树(Prim + Kruaskal)

次小生成树(Prim + Kruaskal)

时间:2023-05-01 09:33:05浏览次数:42  
标签:Prim int MST 最小 生成 Kruaskal maxn include

问题引入:

  我们先来回想一下生成树是如何定义的,生成树就是用n - 1条边将图中的所有n个顶点都连通为一个连通分量,这样的边连成子树称为生成树。

  最小生成树很明显就是生成树中权值最小的生成树,那么我们即将要学的次小生成树或者K小生成树是怎么定义的呢,很明显就是生成树中权值第k小的生成树。

  下面给出刘老师书中对次小生成树的定义,我是用自己的话描述的。

  对于一个无向图G(V, E),其定义了边权为W(u, v),若T为他的一颗最小生成树,那么我们假设存在一颗生成树T1,不存在任意一颗G的生成树T2满足W(T) <= W(T2) < W(T1),那么我们就称T1为

G的次小生成树。

 

非严格次小生成树的求解:

  我们知道最小生成树我们是通过Prim和Kruskal这样的贪心算法求得的,那么次小生成树我们只是对这两种算法进行我们需要的修改就可以进行次小生成树的求解。

  我们很容易可以想到最小生成树和次小生成树应该是有联系的,那么是如何联系的呢?次小生成树就是图G的所有生成树中权值第二小的生成树,也就是说我们只需要替换最小生成树的一条边(u, v)

就可以得到次小生成树,显然这条边肯定不可以属于原最小生成树,如果我们将一条不属于原最小生成树的边(u, v)加入T,那么此时T中就形成了一个环,我们在环中选一个除(u, v)权值最大的边进行删除

(想一下为什么是选择权值最大的那条边),得到的树依然是一颗图G的生成树,我们将所有边逐个加入原最小生成树T,得出并记录所有的生成树的权值T1,那么最后T1中最小的那个值即是次小生成树的

权值。

 

 

下面只对与求解最小生成树中不同的部分进行说明:

Prim:

  我们知道Prim算法是以给定的任意点作为起始点运用一定的方法对所有点进行贪心处理,缩点从而生成一颗最小生成树,那我们只需要用数组用来描述最小生成树中每条边的访问情况以及最小

生成树中每两个顶点之间的最大边权还需要保存最小生成树中每个顶点的父亲顶点,从而就可以方便用于计算次小生成树。

  具体操作:

    初始化:初始化所有点( i )距离最小生成树子树的距离为cost[source][ i ],所有边初始化为未访问,所有顶点之间的最大边权初始化为0。

    加边:每次加入一条安全边(这里不对安全边进行解释,有不了解的可以查阅博主的上一篇博客),并将最小生成子树中顶点之间的最大边权进行更新,接着更新lowc即可。

    求解次小生成树:我们逐一枚举出所有不属于最小生成树的边(u, v),并且用w(u, v)来替代最大边权和Max(u, v),怎么个替代法? 

      SecondMST = min(MST + w(u, v) - Max(u, v))    ((u, v) not belong to MST)。

      OK?

参考代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1000 + 10, INF = 0x3f3f3f3f;
int n, m, lowc[maxn], pre[maxn], Max[maxn][maxn], cost[maxn][maxn];
bool vis[maxn], used[maxn][maxn];

int Prim() {
    int ans = 0;
    memset(vis, false, sizeof vis);
    memset(Max, 0, sizeof Max);
    memset(used, false, sizeof used);
    vis[1] = true;
    pre[1] = -1;
    for(int i = 2; i <= n; i ++) {
        lowc[i] = cost[1][i];
        pre[i] = 1;
    }
    lowc[1] = 0;
    for(int i = 2; i <= n; i ++) {
        int MIN = INF, p = -1;
        for(int j = 1; j <= n; j ++) {
            if(!vis[j] && MIN > lowc[j]) {
                MIN = lowc[j];
                p = j;
            }
        }
        if(MIN == INF)  return -1;
        ans += MIN;
        vis[p] = true;
        used[p][pre[p]] = used[pre[p]][p] = true;
        for(int j = 1; j <= n; j ++) {
            if(vis[j] && j != p) Max[j][p] = Max[p][j] = max(Max[j][pre[p]], lowc[p]);
            if(!vis[j] && lowc[j] > cost[p][j]) {
                lowc[j] = cost[p][j];
                pre[j] = p;
            }
        }
    }
    return ans;
}

int Second_Prim(int MST) {
    int ans = INF;
    for(int i = 1; i <= n; i ++)
        for(int j = i + 1; j <= n; j ++)
            if(!used[i][j] && cost[i][j] != INF) ans = min(ans, MST - Max[i][j] + cost[i][j]);
    return ans;
}

int main() {
    int t, a, b, c;
    scanf("%d", &t);
    while(t --) {
        scanf("%d %d", &n, &m);
        for(int i = 0; i <= n; i ++)
            for(int j = 0; j <= n; j ++)
                cost[i][j] = INF;
        for(int i = 1; i <= m; i ++) {
            scanf("%d %d %d", &a, &b, &c);
            cost[a][b] = cost[b][a] = c;
        }
        int MST = Prim();
        int Second_MST = Second_Prim(MST);
        printf("%d\n", Second_MST);
    }
    return 0;
}

 

 

 

 

 

Kruskal:

  Kruskal算法是将图G的所有边进行排序,从小到大满足边的两个顶点有一个不在subMST中就将其加入MST,在求解次小生成树问题时我们也需要记录MST中结点的连接情况,以及MST中两个顶点

间的最大边权。

  具体操作:

    初始化:初始化并查集,初始化在subMST中每个结点 i 直接或者间接相连的边为i。

    加边:每次加入一条边时,我们更新subMST中所有与u, v相连的结点间的最大边权,接着将所有与结点v相连的边都与结点u也连起来就行了(前提是在合并时head[ head[ v ] ] = head[ u ])。

    求解次小生成树:

      SecondMST = min(MST + w(u, v) - Max(u, v))    ((u, v) not belong to MST)。滑稽.jpg

参考代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 1000 + 10, maxe = 1000 * 1000 / 2 + 5, INF = 0x3f3f3f3f;
int n, m, pre[maxn], head[maxn], Max[maxn][maxn];
struct Edge {
    int u, v, w;
    bool vis;
}edge[maxe];
vector<int> G[maxn];

bool cmp(const Edge &a, const Edge &b) {
    return a.w < b.w;
}

void Init() {
    for(int i = 1; i <= n; i ++) {
        G[i].clear();
        G[i].push_back(i);
        head[i] = i;
    }
}

int Find(int x) {
    if(head[x] == x) return x;
    return head[x] = Find(head[x]);
}

int Kruskal() {
    sort(edge + 1, edge + 1 + m, cmp);
    Init();
    int ans = 0, cnt = 0;
    for(int i = 1; i <= m; i ++) {
        if(cnt == n - 1) break;
        int fx = Find(edge[i].u), fy = Find(edge[i].v);
        if(fx != fy) {
            cnt ++;
            edge[i].vis = true;
            ans += edge[i].w;
            int len_fx = G[fx].size(), len_fy = G[fy].size();
            for(int j = 0; j < len_fx; j ++)
                for(int k = 0; k < len_fy; k ++)
                    Max[G[fx][j]][G[fy][k]] = Max[G[fy][k]][G[fx][j]] = edge[i].w;
            head[fx] = fy;
            for(int j = 0; j < len_fx; j ++)
                G[fy].push_back(G[fx][j]);
        }
    }
    return ans;
}

int Second_Kruskal(int MST) {
    int ans = INF;
    for(int i = 1; i <= m; i ++)
        if(!edge[i].vis)
            ans = min(ans, MST + edge[i].w - Max[edge[i].u][edge[i].v]);
    return ans;
}

int main() {
    int t;
    scanf("%d", &t);
    while(t --) {
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= m; i ++) {
            scanf("%d %d %d", &edge[i].u, &edge[i].v, &edge[i].w);
            edge[i].vis = false;
        }
        int MST = Kruskal();
        int Second_MST = Second_Kruskal(MST);
        printf("%d\n", Second_MST );
    }
    return 0;
}

 

标签:Prim,int,MST,最小,生成,Kruaskal,maxn,include
From: https://www.cnblogs.com/jyssh/p/17366181.html

相关文章

  • 大幅超越DALL·E 2和Imagen,斯坦福发布RA-CM3模型,融合检索与生成
    最近,DALL-E和CM3等模型在多模态任务尤其是图文理解上表现出色。然而,这些模型似乎需要将所有学到的知识存储都存储在模型参数中,这就不得不需要越来越大的模型和训练数据来获取更多的知识,俨然将biggerandbetter绑定在了一起。那既然如此,哪还需要算法工程师?全体转行数据标注工程师和......
  • springboot mybatis-plus 3.5.1代码生成器配置
    springbootmybatis-plus3.5.1代码生成器配置https://blog.csdn.net/Lean_on_Me/article/details/128066822  ......
  • 用ChatGPT生成图片的指令
    接下来我会给你指令,生成相应的图片,我希望你用Markdown语言生成,不要用反引号,不要用代码框,你需要用UnsplashAPI,遵循以下的格式:source.unsplash.com/1600x900/?<将您的查询放在此处>。你明白了吗? ......
  • 使用 ChatGPT 生成 Vue3 响应式导航栏组件
    下面是ChartGPT生成的Vue3响应式导航栏组件。经过简单的调试。基本可实现描述的要求。Nav.vue<template><navclass="nav-container"><divclass="logo-container"><imgsrc="your-logo-here.svg"alt="logo"/></......
  • 生成函数GeneratingFunction
    生成函数GeneratingFunction极限\(\forall\rightarrow\)对于\(\exists\rightarrow\)存在极限:\(\forall\epsilon,\existsN,N>n,|a_n-A|<\epsilon\)就是说,对于所有(任意小的非负整数)\(\epsilon\)存在\(N\),使得\(a_n\)与A的差值小于\(\epsilon\)我们就把\(A\)叫做此序列......
  • 有哪些好用的代码生成工具?
    代码生成工具的历史可以追溯到20世纪50年代,当时的程序员使用手动编写代码的方式来开发软件。随着计算机技术的发展,需要更快速、更高效的代码生成工具来满足开发需求。在20世纪60年代,出现了一些基于规则和模板的代码生成工具,例如:Alice、穆尔和MARY等。在20世纪70年代,Microsoft公司......
  • Pytorch2 如何通过算子融合和 CPU/GPU 代码生成加速深度学习
    动动发财的小手,点个赞吧!PyTorch中用于图形捕获、中间表示、运算符融合以及优化的C++和GPU代码生成的深度学习编译器技术入门计算机编程是神奇的。我们用人类可读的语言编写代码,就像变魔术一样,它通过硅晶体管转化为电流,使它们像开关一样工作,并允许它们实现复杂的逻辑——这......
  • AOP实现将入参与出参写入日志文件中,每天生成一个文件
    LogAspectpackageorg.jeecg.interceptor;importcom.alibaba.fastjson.JSON;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;i......
  • C# 打包项目,.生成安装包
    一、准备工作1VisualStudio2015必须有相关的打包组件;2VisualStudio的打包组件有InstallShield和VisualStudioInstallerProjects(安装包:VSI_bundle)组件;3VisualStudioInstallerProjects还可在VS软件中下载,下载方式如下:a)点中菜单栏的“工具”选项,并选中“扩展和更新......
  • 「学习笔记」重修生成树
    最小生成树(MinimumSpanningTree,MST)为边权和最小的生成树。算法Kruskal算法实现将所有的边按边权从小到大排序,然后用并查集维护一条边所连接的两个点是否已联通(不能形成环)。intfind(intx){ returnfa[x]==x?fa[x]:fa[x]=find(fa[x]);}llkruskal(){ ll......