Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

  • 数学

  • 计算几何

  • 动态规划

  • 图论

    • 连通性相关
    • 二分图
    • 网络流
    • 差分约束
    • 拆点
    • 欧拉回路
    • 最小斯坦纳树
    • 字符串

    • 杂项

    • 算法笔记
    • 图论
    martian148
    2025-03-16
    目录

    最小斯坦纳树

    给出一个无向联通图 G=(V,E)G=(V,E)G=(V,E),和一个包含 kkk 个节点的点集 SSS,包含点集 SSS 的联通子图就是斯坦纳树,那么最小的联通子图,也就是最小斯坦纳树。

    这个问题是 NP- hard,我们只有近似多项式的解法

    image.png

    在这个图中,最小斯坦纳树的值为 11,整棵树长这样:

    image.png

    我们发现除了点集 SSS 中的点,还包含了图上的点 666,所以我们应该思考如何合理利用剩下的 n−kn-kn−k 个点

    我们使用状压 DP 来解决这个问题,定义 f(i,S)f(i,S)f(i,S) 表示以 iii 为根结点的一棵树,包含集合 SSS 中所有点的最小权值和

    那么有两种转移:

    • 枚举 SSS 的一个子集 TTT,f(i,T)+f(i,S−T))→f(i,S)f(i,T)+f(i,S-T))\rightarrow f(i,S)f(i,T)+f(i,S−T))→f(i,S)
    • 枚举 iii 的所有边,f(i,S)+w(j,i)→f(j,S)f(i,S)+w(j,i)\rightarrow f(j, S)f(i,S)+w(j,i)→f(j,S)

    第一个可以枚举子集来转移,时间复杂度是 O(n3k)O(n3^k)O(n3k)

    第二个类似于三角形不等式,可以使用 SPFA 来解决,时间复杂度是 O(nm2k)O(nm2^k)O(nm2k)

    # 代码实现

    洛谷 P6192 【模板】最小斯坦纳树 (opens new window)

    #include <bits/stdc++.h>
    using namespace std;
    constexpr int INF = 0x3f3f3f3f;
    
    int main() {
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        int N, M, K; cin >> N >> M >> K;
        vector<vector<pair<int, int>>> g(N + 1);
        vector<vector<int>> dp(N + 1, vector<int>(1 << K, INF));
        vector<int> p(K + 1);
        for (int i = 1; i <= M; i++)  {
            int u, v, w; cin >> u >> v >> w;
            g[u].push_back({v, w}); g[v].push_back({u, w});
        }
        for (int i = 1; i <= K; i++) {
            cin >> p[i];
            dp[p[i]][1 << (i - 1)] = 0;
        }
        queue<int> q;
        vector<int> vis(N + 1, 0);
        for (int S = 0; S < (1 << K); S++) {
            for (int i = 1; i <= N; i++) {
                for (int S_ = S; S_; S_ = (S_ - 1) & S) {
                    dp[i][S] = min(dp[i][S], dp[i][S_] + dp[i][S ^ S_]);
                }
                if (dp[i][S] < INF)
                    q.push(i), vis[i] = 1;
            }
            while (!q.empty()) {
                int u = q.front(); q.pop(); vis[u] = 0;
                for (auto [v, w] : g[u]) {
                    if (dp[v][S] > dp[u][S] + w) {
                        dp[v][S] = dp[u][S] + w;
                        if (!vis[v])
                            q.push(v), vis[v] = 1;
                    }
                }
            }
        }
        cout << dp[p[1]][(1 << K) - 1] << '\n';
        return 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    上次更新: 2025/04/08, 18:03:31
    欧拉回路
    KMP

    ← 欧拉回路 KMP→

    最近更新
    01
    Java基础语法
    05-26
    02
    开发环境配置
    05-26
    03
    pink 老师 JavaScript 学习笔记
    05-26
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式