最小斯坦纳树
最小斯坦纳树,就是在一个无向连通图要花费最小的代价,连通给定的 个关键点(一般 ),这是一个组合优化问题。
这个问题可以用状压 DP 来解决,首先容易发现一个结论:
答案一定是树。你猜为啥叫最小斯坦纳树。
证明:如果答案存在环,则删去环上任意一条边,代价变小。
于是我们为这棵树钦定一个树根,设 表示以 为根的一棵树,包含集合 中所有点的最小代价(只考虑关键点,即 是关键点集合的子集)。
考虑如何不重不漏地转移。
一棵以 为根的树有两种情况,第一种是 的 ,另一种是 。
对于 的情况,可以考虑枚举树上与 相邻的点 ,则:
对于 的情况,可以划分成几个子树考虑,即:
这里的转移顺序是有讲究的,这可以理解成一个类似背包的 DP,按 (二进制形态)升序枚举即可。
这两种转移具体如何实现呢?对于 式较为简单,枚举子集即可,时间复杂度为 。
对于 式,可以想到最短路的松弛。所以在 式枚举子集后,在同一个 中 个点跑 dij 即可,这部分时间复杂度为 。
所以总时间复杂度为 。
肯定不能用子集卷积优化复杂度,因为是 卷积,做牛魔。
//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;}
这里是点权,所以我们的 DP 转移要改一下:
后者 的原因是被算了两遍。
一样做。
最后 DFS 求方案即可。
//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) const int inf=1e9; int n,m,lim,S,k=0,a[N],f[V][N],pre[V][N]; bool ans[N],vis[N],used[V][N]; vector<int> e[N]; priority_queue<pair<int,int> > q; int num(int x,int y){ return (x-1)*m+y; } void adde(int x,int y){ e[x].pb(y); e[y].pb(x); } void dij(int *dis,int *p){ fill(vis+1,vis+1+lim,false); int x; while(!q.empty()){ x=q.top().sec; q.pop(); if(vis[x]) continue; vis[x]=true; for(int i:e[x]){ if(dis[i]>dis[x]+a[i]){ dis[i]=dis[x]+a[i]; q.push(mkp(-dis[i],i)); p[i]=x; } } } } void dfs(int x,int y){ if(used[x][y]) return ; used[x][y]=true; ans[y]=true; if(pre[x][y]){ int tmp=pre[x][y]; while(tmp){ dfs(x,tmp); tmp=pre[x][tmp]; } }else{ for(int s=x&(x-1);s;s=x&(s-1)){ if(f[x][y]==f[s][y]+f[x^s][y]-a[y]){ dfs(s,y); dfs(x^s,y); break; } } } } signed main(){ios::sync_with_stdio(false),cin.tie(nullptr); cin>>n>>m; lim=n*m; For(i,1,lim) cin>>a[i]; For(i,1,lim) if(!a[i]) k++; if(!k){ cout<<0<<endl; For(i,1,n){ For(j,1,m) cout<<"_"; cout<<endl; } return 0; } S=1<<k; For(i,1,S-1) fill(f[i]+1,f[i]+lim+1,inf); int x=0,rep; For(i,1,n) For(j,1,m){ if(!a[num(i,j)]) f[1<<(x++)][num(i,j)]=0,rep=num(i,j); if(i<n) adde(num(i,j),num(i+1,j)); if(j<m) adde(num(i,j),num(i,j+1)); } For(s,1,S-1){ For(i,1,lim){ for(int ss=s&(s-1);ss;ss=s&(ss-1)) ckmn(f[s][i],f[s^ss][i]+f[ss][i]-a[i]);//double calced a[i] so subtract it if(f[s][i]<inf) q.push(mkp(-f[s][i],i)); } dij(f[s],pre[s]); } cout<<f[S-1][rep]<<endl; dfs(S-1,rep); For(i,1,n){ For(j,1,m){ if(!a[num(i,j)]) cout<<"x"; else if(ans[num(i,j)]) cout<<"o"; else cout<<"_"; } cout<<endl; } return 0;}
本文来自博客园,作者:ShaoJia,版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义