[图论]-------斯坦纳树

斯坦纳树简介

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

斯坦纳树问题的最初形式是这样的:给定平面上的一系列点 P 1 , P 2 , P 3 , . . . , P n P_1,P_2,P_3,...,P_n P1P2P3...Pn,现在要想办法用边将这 n n n 个点连通。允许在平面上添加一些新的点(这些点称为“斯坦纳点”)作为辅助,添加的边的端点可以是原本存在的点,也可以是斯坦纳点,要求这个网络的总长度最小。

例如,当 n = 3 n=3 n=3 时,记三个点为 A ,   B ,   C A,\ B,\ C A, B, C, 有如下结论:如果三角形 A B C ABC ABC 的每个内角都小于 120 ° 120\degree 120°,那么增加一个斯坦纳点 S S S,当 S A   , S B ,   S C SA\ ,SB,\ SC SA ,SB, SC 的夹角都为 120 ° 120\degree 120° 时, S A + S B + S C SA+SB+SC SA+SB+SC 最小。如果三角形 A B C ABC ABC 有一个角大于等于 120 ° 120\degree 120°,那么斯坦纳点与大于 120 ° 120\degree 120° 度的角的顶点重合。
在这里插入图片描述

斯坦纳树问题的定义随着历史的发展不断地推广,后来出现了带点权的斯坦纳树问题。但算法竞赛中似乎没有见到过这种几何意义上的斯坦纳树问题,而是斯坦纳树的思想在图论中的一种体现。

图论中的斯坦纳树问题

图论中的斯坦纳树问题失去了原本的几何意义,实在离散的图上定义的。

给定 N N N 个结点和 M M M 条边的连通图,选取 K K K关键点,需要保留一些边使得 K K K 个关键点连通。要求边权和最小。这 K K K 个点可能不会直接连通,所以需要利用剩下 N − K N-K NK 个非关键点,这些点就是斯坦纳点。

求解斯坦纳树的方法是,状态压缩动态规划。

状态的定义是 f ( i , S ) f(i, S) f(i,S) i i i 表示以 i i i 结点为根。 S S S 是一个整数表示的状态,表示的是 K K K 个关键点的连通信息。也就是 f ( i , S ) f(i, S) f(i,S) 表示以 i i i 为根,包含集合 S S S 中所有点的最小边权值和。

状态转移需要经历两个过程,分别是子集转移和当前状态下的边松弛操作。

  • f ( i , S ) = m i n { f ( i , S )   ,   f ( i , T ) + f ( i ,   S − T ) } f(i, S)=min\{f(i,S)\ ,\ f(i,T)+f(i,\ S-T)\} f(i,S)=min{f(i,S) , f(i,T)+f(i, ST)}

其中 T T T S S S 的一个子集, S − T S-T ST 表示 T T T 的补集。子集转移需要枚举状态 S S S 的所有子集,枚举子集可以用如下方法:

for(int sub=S&(S-1);sub;sub=S&(sub-1))
{
	//sub 就是从大到小枚举的 S的子集 
	//S^sub 就是sub的补集
}

可以证明这样做的正确性:首先显然 S & ( s u b − 1 ) S\&(sub-1) S&(sub1) 一定是 S S S 的子集,所以只需证明 s u b sub sub ( s u b − 1 ) & S (sub-1) \& S (sub1)&S 之间没有 S S S 的子集。
减一相当于将 s u b sub sub 最右边的 1 变成 0,同时将这个 1 左边的 0 都变成 1。所以 s u b & ( s u b − 1 ) sub\&(sub-1) sub&(sub1) 一定是小于 s u b sub sub 的最大的 S S S 的子集。

  • f ( i , S ) = m i n { f ( i , S )    ,    f ( j , S ) + w ( i , j ) } f(i, S)=min\{f(i,S)\ \ ,\ \ f(j,S)+w(i,j)\} f(i,S)=min{f(i,S)  ,  f(j,S)+w(i,j)}

可以发现这部分的转移是在同一个状态 S S S 下进行的,参与转移的只有边权,实际上就转化为了最短路问题,可以直接用 D i j k s t r a Dijkstra Dijkstra S P F A SPFA SPFA 来转移。

算法核心代码如下:

memset(dp, INF, sizeof(dp));
for(int i=1;i<=K;i++)
{
	scanf("%d", &key[i]);       //第i个关键点
	dp[key[i]][1<<(i-1)] = 0;
}
for(int S=1;S<(1<<K);S++)       //枚举K个关键点的所有状态 
{
	for(int i=1;i<=N;i++)       //枚举根节点 
	{
		for(int sub=S&(S-1);sub;sub=S&(sub-1))     //枚举 S 的所有子集
		    dp[i][S] = min(dp[i][S], dp[i][sub]+dp[i][S^sub]);
		if(dp[i][S]!=INF)
		    q.push((t_Node){dp[i][S], i});         //最短路的优先队列 
	}
	Dijkstra(S);                //对状态 S 进行状态内转移 
}

实际问题中 K K K 不会太大,我猜这个问题可能是 N P NP NP 的,没有关于 K K K 的多项式时间算法。

斯坦纳森林

有时候问题并不要求 K K K 个关键点全部连通,而是满足某些要求的点连通即可,也就是说连通后的图为多棵斯坦纳树组成的斯坦纳森林。

求解斯坦纳森林首先要求出正常的斯坦纳树的结果,然后对斯坦纳树再进行一次状态压缩。

f ( S ) f(S) f(S) 表示关键点连通状态为 S S S 的最小边权和。枚举 S S S 的子集进行转移,但前提是 S S S 需要满足题目所给的要求。

memset(f, INF, sizeof(f));
for(int S=1;S<1<<K;S++)
{
	if(check(S))         //检查状态S是否满足要求 
	{
		for(int i=1;i<=N;i++)
		    f[S] = min(f[S], dp[i][S]);
	}
}
for(int S=1;S<S<1<<K);S++)
{
	for(int sub=S&(S-1);sub;sub=S&(sub-1))
	{
		if(check(sub)&&check(S^sub))
		    f[S] = min(f[S], f[sub]+f[S^sub]);
	}
}
printf("%d\n", f[(1<<K)-1]);

例题

模板:
洛谷P6192 【模板】最小斯坦纳树.

思维题+斯坦纳树:
2018-2019 ACM-ICPC Brazil Subregional Programming Contest J - Joining Capitals.

点权斯坦纳树,带路径输出:
洛谷P4294 [WC2008]游览计划.

斯坦纳森林:
UVA1496 Peach Blossom Spring.

洛谷P3264 [JLOI2015]管道连接.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值