学习笔记——斯坦纳树

学习,笔记,斯坦纳 · 浏览次数 : 0

小编点评

**斯坦纳树问题**是一种组合优化问题,它是在给定的点集和边中寻求最短网络使所有点连通的方法。 **最小生成树**是一种特殊的斯坦纳树,它允许在给定点外增加额外的点,使生成的最短网络开销最小。 **斯坦纳树**是一种树数据结构,它允许在给定的点集和边中寻找最短网络使所有点连通。然而,最小生成树允许在给定点外增加额外的点,使生成的最短网络开销最小。 **如何生成最小生成树?** 1. 初始化一个优先队列,并将所有给定的点加入队列中。 2. 遍历队列,并对于每个点,计算以该点为根的最小生成树的边权和。 3. 选择最小的边权和的点作为生成树的根节点。 4. 从队列中删除所有与当前点相关的点。 5. 重复步骤 2 和 3,直到队列为空。 6. 生成树的根节点的所有子节点,并设置其父节点的指向指向其子节点。 7. 返回生成树。 **时间复杂度:**O(V log V),其中 V 是点的数量。 **代码:** ```cpp #include <bits/stdc++.h>#define pii pair<int, int>#define INF 0x3f3f3f3f#define int long long#define M 5010#define N 510using namespace std;inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();} while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return x * f;}int n, m, k, cnt, head[M], f[N][M], vis[N], key[N];struct node{int u, v, w, next;}e[N << 1];priority_queue<pii, vector<pii>, greater<pii> > q;inline void add(int u, int v, int w){e[++ cnt] = (node){u, v, w, head[u]}; head[u] = cnt;}inline void Dij(int s){ memset(f, INF, sizeof f); n = read(), m = read(), k = read(); for(int i = 1; i <= m; i ++) { int u = read(), v = read(), w = read(); add(u, v, w); add(v, u, w); } for(int i = 1; i <= k; i ++) { key[i] = read(); f[key[i]][1 << (i - 1)] = 0;//以关建点为根,只包含当前点的代价是0 } for(int s = 1; s <= (1 << k); s ++)//枚举k个点的所有状态 { for(int i = 1; i <= n; i ++)//枚举每一个点 { for(int subs = s & (s - 1); subs; subs = s & (subs - 1))//枚举每一个子集 f[i][s] = min(f[i][s], f[i][subs] + f[i][s ^ subs]);//DP取最小值 if(f[i][s] != INF) q.push({f[i][s], i});//只要不是全是INF就可以试试 } Dij(s);//跑DIJ } cout << f[key[1]][(1 << k) - 1] << endl;//输出以1为根的子树所有关键点都在里面最小的边权和 return 0;}signed main(){ memset(f, INF, sizeof f); n = read(), m = read(), k = read(); for(int i = 1; i <= m; i ++) { int u = read(), v = read(), w = read(); add(u, v, w); add(v, u, w); } for(int i = 1; i <= k; i ++) { key[i] = read(); f[key[i]][1 << (i - 1)] = 0;//以关建点为根,只包含当前点的代价是0 } for(int s = 1; s <= (1 << k); s ++)//枚举k个点的所有状态 { for(int i = 1; i <= n; i ++)//枚举每一个点 { for(int subs = s & (s - 1); subs; subs = s & (subs - 1))//枚举每一个子集 f[i][s] = min(f[i][s], f[i][subs] + f[i][s ^ subs]);//DP取最小值 if(f[i][s] != INF) q.push({f[i][s], i});//只要不是全是INF就可以试试 } Dij(s);//跑DIJ } cout << f[key[1]][(1 << k) - 1] << endl;//输出以1为根的子树所有关键点都在里面最小的边权和 return 0;} ```

正文

斯坦纳树

斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。-------- 百度百科

在图论里,一般用于解决形如:

给定一个连通图 \(G\),给定 \(k\) 个关键点,选取一些边使得 \(k\) 个关键点连通,要求权值和最小。

最小生成树可以看作是一种特殊的斯坦纳树。

我们不难想到,给出的 \(k\) 个点可能并不能只用连接这 \(k\) 个点的边达到连通,有可能需要另外 \(n-k\) 个点的中转;其次,可能经过外面 \(n-k\) 个点中的点的中转,可以让边权和更小。

P6192 【模板】最小斯坦纳树

我们发现 \(k\) 很小,所以可以使用状压来试试。

我们用 \(f[i][S]\) 表示以 \(i\) 为根的树,包含 \(S\) 内所有点的最小边权值和。

首先我们知道最后形成的是一棵树,不难想到最后有个环的话去掉最大的那条边也可以。

我们考虑如何转移状态:

  • \(f[i][s] = min(f[i][s], f[i][s] + f[i][s \text{^} subs])\)

其中 \(subs\)\(s\) 状态的一个子集,这个是保证一开始 \(f[i][s]\) 尽量小。

  • 跑最短路,通过 \(f[v][s] = f[u][s] + e[i].w\) 来进行松弛,处理以所有点为根,子树包含 \(s\) 状态个关键点的最小边权和。
/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-02 17:28:45
 * @LastEditTime: 2023-10-02 19:27:11
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\P6192.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include <bits/stdc++.h>

#define pii pair<int, int>
#define INF 0x3f3f3f3f
#define int long long
#define M 5010
#define N 510

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int n, m, k, cnt, head[M], f[N][M], vis[N], key[N];
struct node{int u, v, w, next;}e[N << 1];
priority_queue<pii, vector<pii>, greater<pii> > q;

inline void add(int u, int v, int w){e[++ cnt] = (node){u, v, w, head[u]}; head[u] = cnt;}

inline void Dij(int s)
{
    memset(vis, 0, sizeof vis);//清空vis
    while(!q.empty())
    {
        int u = q.top().second;//取出当前点
        q.pop();
        if(vis[u]) continue;//如果之前找过了就不用遍历了
        vis[u] = 1;//打标记
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].v;//终点
            if(f[v][s] <= f[u][s] + e[i].w) continue;//如果加入了这条边边权和更大了就不加
            f[v][s] = f[u][s] + e[i].w;//松弛
            q.push({f[v][s], v});//入堆
        }
    }
    return ;
}

signed main()
{
    memset(f, INF, sizeof f);
    n = read(), m = read(), k = read();
    for(int i = 1; i <= m; i ++)
    {
        int u = read(), v = read(), w = read();
        add(u, v, w);
        add(v, u, w);
    }
    for(int i = 1; i <= k; i ++)
    {
        key[i] = read();
        f[key[i]][1 << (i - 1)] = 0;//以关建点为根,只包含当前点的代价是0
    }
    for(int s = 1; s <= (1 << k); s ++)//枚举k个点的所有状态
    {
        for(int i = 1; i <= n; i ++)//枚举每一个点
        {
            for(int subs = s & (s - 1); subs; subs = s & (subs - 1))//枚举每一个子集
                f[i][s] = min(f[i][s], f[i][subs] + f[i][s ^ subs]);//DP取最小值
            if(f[i][s] != INF) q.push({f[i][s], i});//只要不是全是INF就可以试试
        }
        Dij(s);//跑DIJ
    }
    cout << f[key[1]][(1 << k) - 1] << endl;//输出以1为根的子树所有关键点都在里面最小的边权和
    return 0;
}

参考博客:https://www.luogu.com.cn/blog/ydz2010/si-tan-na-shu

与学习笔记——斯坦纳树相似的内容:

学习笔记——斯坦纳树

斯坦纳树 斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。 百度百科 在图论里,一般用于解决形如: 给定一个连通图 \(G\),给定 \(k\) 个关键点,选取

学习笔记

周屹梁的学习笔记 个人各平台地址 博客地址:https://www.cnblogs.com/zylyehuo/ gitee地址:https://gitee.com/zylyehuo github地址:https://github.com/zylyehuo 夯实基础 四元数法 | 代价地图组成(多层叠

[转帖]【学习笔记】Linux下CPU性能评估

Linux下CPU性能评估 1、 vmstat监控CPU使用情况 【说明】 procs: l r表示运行和等待CPU时间片的进程数,这个值如果长期大于系统CPU的个数,就说明CPU不足,需要增加CPU。 l b表示在等待资源的进程数,比如正在等待I/O或者内存交换等。 memory: l swpd:

【学习笔记】Python 使用 matplotlib 画图

目录安装中文显示折线图、点线图柱状图、堆积柱状图坐标轴断点参考资料 本文将介绍如何使用 Python 的 matplotlib 库画图,记录一些常用的画图 demo 代码 安装 # 建议先切换到虚拟环境中 pip install matplotlib 中文显示 新版的 matplotlib 已经支持

【学习笔记】基础算法:二次离线莫队/回滚莫队

【学习笔记】基础算法:二次离线莫队/回滚莫队 二次离线莫队 前置知识:莫队 前置知识:值域分块 值域分块,就是对 \(A\) 的值域进行分块,每个块维护该值域内数的个数 众所周知,莫队的复杂度是 \(O(n \sqrt m)\) 的,而在维护一些问题时左右端点移动一格并不是 \(\mathcal O

算法学习笔记(29):分块

分块 这是一种基于根号的算法,核心为大块标记,散块暴力,做到复杂度的平衡。 可能第一个想到于此相关的就是莫队吧,这是利用分块优化暴力的方法。 目录分块Rmq Problem / mex[国家集训队] 排队 - 洛谷[TJOI2009] 开关 - 洛谷[Violet] 蒲公英 - 洛谷小小总结 Rmq

VisionPro学习笔记(1)——软件介绍和基本使用

如果需要了解其他图像处理的文章,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 前言 自己使用visionPro已经有段时间了,最近也一直在研究其算子的理论,为了加深印象,计划将

pytorch学习笔记——timm库

当使用ChatGPT帮我们工作的时候,确实很大一部分人就会失业,当然也有很大一部分人收益其中。我今天继续使用其帮我了解新的内容,也就是timm库。毫不夸张的说,Chat GPT比百分之80的博客讲的更清楚更好,仅次于源码。 当提到计算机视觉的深度学习框架时,PyTorch无疑是最受欢迎的选择之一。P

VisionPro学习笔记(2)——图像转换工具ImageCovertTool

如果需要了解其他图像处理的文章,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 前言 众所周知,VisionPro是一款功能强大的机器视觉软件,用于开发和部署机器视觉应用程序。其

VisionPro学习笔记(3)——BeadInspectTool

如果需要了解其他图像处理的文章,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice VisionPro有很多的示例和算子,这里再展示一个最新出的算子Bead Inspect Tool