概念
P6192 【模板】最小斯坦纳树
对于无向图 G=(V,E),记其关键点集合为 S。试在图中选出一边集 E′,使得图 G′=(V′,E′)(其中 S∈V′)为连通图且 ∑(i,j)∈E′wi,j 最小。易看出满足条件的图 G′ 为一棵树(反之删去环上任意一边边权总和更小),符合以上性质的生成树称为最小斯坦纳树。
思路
当 |S| 较小时,最小斯坦纳树可以通过状压 dp 求解。
考虑令 f[i][s] 表示以 i 为生成树的树根,生成树中的关键点集合为 s 时的最小边权总和。分类讨论:
当 i 有多棵子树时,考虑枚举最后添加的一棵子树,有 ∀s′∈s,f[i][s]=min(f[i][s−s′]+f[i][s′])。直接枚举子集的复杂度是 O(3n)
当 i 只有一棵子树时,易知 ∀(i,j)∈E,f[i][s]=min(f[j][s]+wi,j)。联想到最短路,容易看出这个式子实际上可以通过松弛转移(以 f[][s] 为距离数组)。据说用 SPFA 会比 Dijkstra 快(不知道什么原理
时间复杂度是 O(2|S|∑i=03i+nlogn)
代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 1e2 + 5;
const int maxm = 1e3 + 5;
const int maxk = 15;
const int maxs = (1 << 10);
const int inf = 0x3f3f3f3f;
struct node
{
int to, nxt, w;
} edge[maxm];
int n, m, k, cnt;
int s[maxk];
int head[maxn], f[maxn][maxs];
bool in_queue[maxn];
queue<int> q;
void add_edge(int u, int v, int w)
{
cnt++;
edge[cnt].to = v;
edge[cnt].nxt = head[u];
edge[cnt].w = w;
head[u] = cnt;
}
void spfa(int st)
{
while (!q.empty())
{
int u = q.front();
q.pop();
in_queue[u] = false;
for (int i = head[u]; i; i = edge[i].nxt)
{
int v = edge[i].to;
if (f[v][st] > f[u][st] + edge[i].w)
{
f[v][st] = f[u][st] + edge[i].w;
if (!in_queue[v])
{
q.push(v);
in_queue[v] = true;
}
}
}
}
}
int main()
{
int u, v, w;
memset(f, 0x3f, sizeof(f));
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
add_edge(v, u, w);
}
for (int i = 1; i <= k; i++)
{
scanf("%d", &s[i]);
f[s[i]][(1 << (i - 1))] = 0;
}
for (int i = 0; i < (1 << k); i++)
{
for (int j = 1; j <= n; j++)
for (int l = i & (i - 1); l; l = (l - 1) & i)
f[j][i] = min(f[j][i], f[j][i ^ l] + f[j][l]);
for (int j = 1; j <= n; j++)
{
if (f[j][i] != inf)
{
q.push(j);
in_queue[j] = true;
}
}
spfa(i);
}
printf("%d\n", f[s[1]][(1 << k) - 1]);
return 0;
}
变式
最小斯坦纳树森林
P3264 [JLOI2015]管道连接
每个关键点有其类型,要求同一类型的关键点连通即可。
可以考虑分别考虑每种类型,处理出使该类型的关键点全部连通的最小代价。令 ws 表示令集合 s 中的关键点全部连通的最小代价,其中 s 为显然有 ∀1≤i≤n,ws=min(f[i][s])
最后考虑合并代价。令 gs 表示使集合 s 中的关键点全部连通的最小代价,且 s 满足:若 s 包含某一类型的关键点,则该类型的所有关键点均在 s 中。有:∀s′∈s,gs=min(gs−s′+ws′)
由于上面考虑了所有情况的最优解,因此不会重复选边。
最终答案为 g2k−1
点权最小斯坦纳树
P4294 [WC2008]游览计划
要求 ∑u∈V′wu 最小。
状态照旧,考虑转移。
当 u 有多棵子树时,直接转移会重复累加 wu,所以方程为
∀s′∈s,f[u][s]=min(f[u][s−s′]+f[u][s′]−wu)
当 u 有一棵子树时,有
∀(u,v)∈E,f[u][s]=min(f[v][s]+wu)
构造最小斯坦纳树
考虑直接记录更新当前状态的状态,然后递归处理。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现