Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 数论

  • 动态规划

  • 暴力

  • 图论

    • 2-SAT学习笔记
    • 最短路学习笔记
    • KM学习笔记
    • 一般图最大匹配学习笔记
    • 二分图匹配学习笔记
    • 可撤销并查集学习笔记
    • 多源最短路学习笔记
    • 斯坦纳树学习笔记
      • 洛谷 P6192
        • 解法
        • AC代码
    • 最小异或生成树学习笔记
    • LCA学习笔记
    • 树的直径与重心
    • 树的重心
    • 矩阵树定理学习笔记
    • 网络流-最小割学习笔记
    • 网络流-最小费用最大流学习笔记
  • 数据结构

  • 字符串

  • 几何

  • 博弈论

  • 其他

斯坦纳树学习笔记

# 斯坦纳树

一个带权无向图G(V,E)G(V,E)G(V,E)上,令点集V′⊆VV'\subseteq VV′⊆V为关键点集,要联通所有关键点最小的代价。

该问题为公认的NPC问题,目前没有找到多项式复杂度的解!

以下算法只能解决关键点数较少时的问题∣V′∣≤20\mid V'\mid \le 20∣V′∣≤20

# 洛谷 P6192

给定一个包含 nnn 个结点和 mmm 条带权边的无向连通图 G=(V,E)G=(V, E)G=(V,E) 再给定包含 kkk 个结点的点集 S,S,S, 选出 GGG 的子图 G′=(V′,E′),G^{\prime}=\left(V^{\prime}, E^{\prime}\right),G′=(V′,E′), 使得:

  1. S⊆V′S \subseteq V^{\prime}S⊆V′

  2. G′G^{\prime}G′ 为连通图;

  3. E′E^{\prime}E′ 中所有边的权值和最小。

你只需要求出 E′E^{\prime}E′ 中所有边的权值和。

保证给出的无向图连通,但可能存在重边和自环。

# 解法

显然最后得出的图一定是一颗树

记dp[S′][i]dp[S'][i]dp[S′][i]为以iii为根,联通点集S′S'S′的最小代价,则有一下两种转移方式

  1. dp[S′][i]=dp[T][i]+dp[S′−T][i]dp[S'][i]=dp[T][i]+dp[S'-T][i]dp[S′][i]=dp[T][i]+dp[S′−T][i],缩小问题范围
  2. dp[S′][i]=dp[S′][j]+dist(i,j)dp[S'][i]=dp[S'][j]+dist(i,j)dp[S′][i]=dp[S′][j]+dist(i,j)

遍历集合S′⊆SS'\subseteq SS′⊆S,对于每一个子集sss,先进行第一类转移,再进行第二类转移。

# AC代码

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define int128 __int128_t
#define Android ios::sync_with_stdio(false), cin.tie(NULL)
#define redirect_input freopen("./input.txt", "r", stdin);
#define redirect_output freopen("./output.txt", "w", stdout);
#define debug(s, r) std::cerr << #s << ": " << (s) << (r==0?' ':'\n')
#define pii pair<int, int>  
#define sqr(x) ((x)*(x))
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 100 + 10;
int n, m, k;
int st[maxn];
int dp[(1<<10) + 100][maxn];
vector<pii> G[maxn];

void dijkstra(int s){
    priority_queue<pii, vector<pii>, greater<pii> > que;
    for(int i=1; i<=n; i++) {
        if(dp[s][i] != inf) que.push({dp[s][i], i});
    }
    while(!que.empty()){
        pii cur = que.top(); que.pop();
        int u = cur.second;
        if(dp[s][u] != cur.first) continue;
        for(pii edge:G[u]){
            int v = edge.first;
            if(dp[s][v] > dp[s][u] + edge.second){
                dp[s][v] = dp[s][u] + edge.second;
                que.push({dp[s][v], v});
            }
        }
    }
}

void solve(){
    memset(dp, 0x3f, sizeof dp);

    cin >> n >> m >> k;
    for(int i=0, u, v, w; i<m; i++){
        cin >> u >> v >> w;
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    for(int i=0; i<k; i++) cin >> st[i];

    for(int i=0; i<k; i++)
        dp[1<<i][st[i]] = 0;
    
    for(int s=1; s<(1<<k); s++){ 
        for(int subs = s&(s-1); subs; subs = s & (subs-1)){ // 第二类转移
            for(int i=1; i<=n; i++){
                dp[s][i] = min(dp[s][i], dp[subs][i] + dp[subs ^ s][i]);
            }
        }
        dijkstra(s); // 第一类转移
    }
    cout << dp[(1<<k)-1][st[rand() % k]] << '\n';
}

signed main(){
    Android;
    solve();
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
上次更新: 2021/02/24, 03:37:30
多源最短路学习笔记
最小异或生成树学习笔记

← 多源最短路学习笔记 最小异或生成树学习笔记→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式