【模板】斯坦纳树
模拟赛出了一个斯坦纳森林的题,全军覆没。
于是赶紧过来学习。
概述
先来看这道题:一张无向图,求它的最小生成树。
不会有人不会吧,Kruskal
或者Prim
一下就好了。
然后是它的进阶版:
一张无向图
可以发现最小生成树就是
解法
首先,
要不然如果有环的话随便断掉一条边还是符合的。
于是我们钦定一个树根,设
然后转移的时候分两种情况:
- 树中
的度不为 1。
那么一定可以将
但我们不知道
所以有
- 树中
的度为 1。
那么这时与
但是我们仍然不知道
所以又有
可以证明,只有
好像有点问题。建议不要管这个暴力,直接看下面最短路的方法 😃
于是做完了。
注意事项:
总复杂度
但是我们还有一种做法。
有些恶心的出题壬会把
观察第二种情况的式子:
既然要优化,我们就限制一下
然后我们发现:
这和最短路的松弛操作长的一模一样。
于是我们就可以愉快的用 dijkstra
或 SPFA
(只有正权)优化了。
总复杂度
例题:
就是板子。
Floyd
// Problem: P6192 【模板】最小斯坦纳树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6192
// Memory Limit: 250 MB
// Time Limit: 1000 ms
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i,a,b) for(ll i=a;i<=b;i++)
#define wt int tt=d;while(tt--)
#define py puts("Yes")
#define pn puts("No")
#define fe(i,e) for(int i=0;i<e.size();i++)
#define vi vector<ll>
inline ll rd() {
ll x=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return x*f;
}
#define d rd()
#define pb push_back
const ll N=300010;
ll dis[110][110];
ll qp(ll a,ll b,ll p){
ll ans=1;while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;b>>=1;
}return ans;
}ll n,m,k;
ll s,dp[110][2010];
int main(){
memset(dis,0x3f,sizeof(dis));
n=d,m=d,k=d;
f(i,1,m){
ll u=d,v=d,w=d;
dis[u][v]=min(dis[u][v],w);
dis[v][u]=min(dis[v][u],w);
}f(kk,1,n)f(i,1,n)f(j,1,n)dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);
memset(dp,0x3f,sizeof(dp));
f(i,1,k)s=d,dp[s][1<<(i-1)]=0;
f(msk,0,(1<<k)-1){
f(i,1,n)for(int ms=msk;ms;ms=(ms-1)&msk)dp[i][msk]=min(dp[i][msk],dp[i][ms]+dp[i][msk^ms]);
f(i,1,n)f(j,1,n)dp[i][msk]=min(dp[i][msk],dp[j][msk]+dis[i][j]);
}printf("%lld\n",dp[s][(1<<k)-1]);
return 0;
}
题意:一张图,给定 4 对关键点(一共 8 个点),求最小斯坦纳森林。
发现按照上面的那样做不行,因为这个不用保证所有点连通。
因为所有的点都是按照顺序读进来的,所以只有第一对点在
所以只有
然后就可以写一个 check
来判断是否合法。
我们再设一个数组
所以一开始
假设
于是可以发现
于是又是一个枚举子集。
然后好了。
// Problem: Ticket to Ride
// Contest: POJ - Northwestern Europe 2006
// URL: http://poj.org/problem?id=3123
// Memory Limit: 65 MB
// Time Limit: 2000 ms
#include<cstring>
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
using namespace std;
#define ll long long
#define f(i,a,b) for(ll i=a;i<=b;i++)
#define wt int tt=d;while(tt--)
#define py puts("Yes")
#define pn puts("No")
#define fe(i,e) for(int i=0;i<e.size();i++)
#define vi vector<ll>
inline ll rd() {
ll x=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return x*f;
}
#define d rd()
#define pb push_back
const ll N=300010;
struct edge{ll v,w,nx;}e[N<<1];
ll hd[N],cnt;
void add(ll u,ll v,ll w){e[++cnt]=(edge){v,w,hd[u]};hd[u]=cnt;}
ll qp(ll a,ll b,ll p){
ll ans=1;while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;b>>=1;
}return ans;
}ll n,m,k;
string s,t;
ll dis[50][50],ans;
map<string,ll>mp;
ll dp[50][500];
ll dpp[500];
bool ch(ll x){
f(i,1,4){
ll p=x-(x>>2<<2);
if(p==3||p==0);
else return 0;
x>>=2;
}return 1;
}
int main(){
while(1){
n=d,m=d,k=8;if(n+m==0)break;mp.clear();
f(i,1,n)f(j,1,n)dis[i][j]=9999999;
f(i,1,n)cin>>s,mp[s]=i;
f(i,1,m){
cin>>s>>t;
ll u=mp[s],v=mp[t],w=d;
dis[u][v]=min(dis[u][v],w);
dis[v][u]=min(dis[v][u],w);
}f(kk,1,n)f(i,1,n)f(j,1,n)dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);
memset(dp,0x3f,sizeof(dp));
f(i,1,k){cin>>s;
dp[mp[s]][1<<(i-1)]=0;
}memset(dpp,0x3f,sizeof(dpp));
f(msk,0,(1<<k)-1){
f(i,1,n)for(int ms=msk;ms;ms=(ms-1)&msk)dp[i][msk]=min(dp[i][msk],dp[i][ms]+dp[i][msk^ms]);
f(i,1,n)f(j,1,n)dp[i][msk]=min(dp[i][msk],dp[j][msk]+dis[i][j]);
}
f(i,0,(1<<k)-1)f(j,1,n)dpp[i]=min(dpp[i],dp[j][i]);
f(i,0,(1<<k)-1)if(ch(i))
for(int j=i;j;j=(j-1)&i)if(ch(j))dpp[i]=min(dpp[i],dpp[j]+dpp[i^j]);
printf("%lld\n",dpp[(1<<k)-1]);
}
return 0;
}
都是板子,加个输出方案就行了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】