luogu P6192 【模板】最小斯坦纳树
题面传送门
看到k这么小容易想到状压。
设\(dp_{i,S}\)为以\(i\)为当前要拓展的节点,包含集合为\(S\)的最小值。
那么就可以两种拓展,就是这个点在最终答案上入度为\(1\),那么就找一条边走出去。
如果这个点入度不是\(1\),那么就划分成两个子树。
然后按照\(S\)正序枚举,第一种可以堆优化dj跑一下,第二种子集枚举即可。
时间复杂度\(O(2^k\times nlogm+3^k\times n)\)
code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db double
#define W 10000000
#define N 100
#define K (1<<10)
#define eps (1e-6)
#define mod 1000000007
using namespace std;
int n,m,k,dp[K+5][N+5],d[N+5],x,y,z,ans=1e9;
struct ques{
int to,w;
bool operator <(const ques &x)const{return w>x.w;}
}now;
struct yyy{int to,w,z;}tmp;
struct ljb{
int head,h[N+5];yyy f[N+5<<4];
I void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]};h[x]=head;}
}s;
priority_queue<ques> q;
int main(){
freopen("1.in","r",stdin);
re int i,j,h;scanf("%d%d%d",&n,&m,&k);for(i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),s.add(x,y,z),s.add(y,x,z);memset(dp,0x3f,sizeof(dp));
for(i=1;i<=k;i++) scanf("%d",&x),dp[1<<i-1][x]=0;
for(i=1;i<(1<<k);i++){
for(j=1;j<=n;j++){
for(h=(i-1)&i;h;h=(h-1)&i)dp[i][j]=min(dp[i][j],dp[h][j]+dp[i^h][j]);
}
for(j=1;j<=n;j++) d[j]=dp[i][j],q.push((ques){j,d[j]});
while(!q.empty()){
now=q.top();q.pop();
for(j=s.h[now.to];j;j=tmp.z)tmp=s.f[j],d[tmp.to]>d[now.to]+tmp.w&&(d[tmp.to]=d[now.to]+tmp.w,q.push((ques){tmp.to,d[tmp.to]}),0);
}
for(j=1;j<=n;j++) dp[i][j]=d[j];
}
for(i=1;i<=n;i++) ans=min(ans,dp[(1<<k)-1][i]);printf("%d\n",ans);
}

浙公网安备 33010602011771号