ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

重修 最小斯坦纳树

2022-08-17 15:01:45  阅读:116  来源: 互联网

标签:fir vis int 重修 最小 斯坦纳 define dis


转自(稍加修改)

最小斯坦纳树,就是在一个无向连通图要花费最小的代价(边权和),连通给定的 \(k\) 个关键点(一般 \(k\le 10\)),这是一个组合优化问题。

这个问题可以用状压 DP 来解决,首先容易发现一个结论:

答案一定是树。你猜为啥叫最小斯坦纳树。

证明:如果答案存在环,则删去环上任意一条边,代价变小。

于是我们为这棵树钦定一个树根,设 \(f(S,i)\) 表示以 \(i\) 为根的一棵树,包含集合 \(S\) 中所有点的最小代价(只考虑关键点,即 \(S\) 是关键点集合的子集)。

考虑如何不重不漏地转移。

一棵以 \(i\) 为根的树有两种情况,第一种是 \(i\) 的 \(deg=1\),另一种是 \(deg>1\)。

对于 \(deg=1\) 的情况,可以考虑枚举树上与 \(i\) 相邻的点 \(j\),则:

\[f(S,j)+w(i,j)\to f(S,i)\quad(A) \]

对于 \(deg>1\) 的情况,可以划分成几个子树考虑,即:

\[f(T,i)+f(S-T,i)\to f(S,i)\quad(T\subsetneq S\land T\ne \varnothing)\quad(B) \]

这里的转移顺序是有讲究的,这可以理解成一个类似背包的 DP,按 \(S\)(二进制形态)升序枚举即可。

这两种转移具体如何实现呢?对于 \((B)\) 式较为简单,枚举子集即可,时间复杂度为 \(O(3^k n)\)。

对于 \((A)\) 式,可以想到最短路的松弛。所以在 \((B)\) 式枚举子集后,在同一个 \(S\) 中 \(n\) 个点跑 dij 即可,这部分时间复杂度为 \(O(2^km\log m)\)。

所以总时间复杂度为 \(O(3^kn+2^km\log m)\)。

P6192 【模板】最小斯坦纳树

//We'll be counting stars.
//#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define mkp make_pair
#define pb emplace_back
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define ckmx(a,b) a=max(a,b)
#define ckmn(a,b) a=min(a,b)
#define debug(...) cerr<<"#"<<__LINE__<<": "<<__VA_ARGS__<<endl
#define N 101
#define V (1<<10)
#define pi pair<int,int>
const int inf=1e9;
int n,m,k,S,f[V][N];
vector<pi> e[N];
priority_queue<pi> q;
bool vis[N];
void dij(int *dis){
	fill(vis+1,vis+1+n,false);
	int x;
	while(!q.empty()){
		x=q.top().sec;
		q.pop();
		if(vis[x]) continue;
		vis[x]=true;
		for(auto i:e[x]){
			if(dis[i.fir]>dis[x]+i.sec){
				dis[i.fir]=dis[x]+i.sec;
				q.push(mkp(-dis[i.fir],i.fir));
			}
		}
	}
}
signed main(){ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>n>>m>>k;
	S=1<<k;
	int x,y,z;
	For(i,1,m){
		cin>>x>>y>>z;
		e[x].pb(mkp(y,z));
		e[y].pb(mkp(x,z));
	}
	For(i,1,S-1) fill(f[i]+1,f[i]+1+n,inf);
	For(i,1,k){
		cin>>x;
		f[1<<(i-1)][x]=0;
	}//dont change x after this (x for a representative)
	For(s,1,S-1){
		For(i,1,n){//f(S,i)<-f(T,i)+f(S^T,i) (T|S=S)
			for(int ss=s&(s-1);ss;ss=s&(ss-1))
				ckmn(f[s][i],f[ss][i]+f[s^ss][i]);
			if(f[s][i]<inf) q.push(mkp(-f[s][i],i));
		}
		dij(f[s]);//f(S,i)<-f(S,j)+w(i,j)
	}
	cout<<f[S-1][x]<<endl;
return 0;}

标签:fir,vis,int,重修,最小,斯坦纳,define,dis
来源: https://www.cnblogs.com/shaojia/p/16595220.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有