BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树
题意:
给定n个点,m条双向边的图。其中有k个点是重要的。每条边都有一定的长度。
现在要你选定一些边来构成一个图,要使得k个重要的点相互连通,求边的长度和的最小值。
分析:
斯坦纳树裸题
dis[i][j]表示关键点连通状态为i,当前在点j的最小花费
有两个转移:内部枚举子集,外部spfa转移
这道题卡spfa,那我们用dij就好啦
代码:
#include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define N 100050 #define LL long long priority_queue <pair <LL,int> >q; int head[N],to[N<<2],nxt[N<<2],cnt; int n,m,k,id[10],vis[33][N]; LL dis[33][N],val[N<<2]; inline void add(int u,int v,LL w){ to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;val[cnt]=w; } int main(){ scanf("%d%d%d",&n,&k,&m); int i,j,mask=(1<<k)-1,p,x,y; LL z; memset(dis,0x3f,sizeof(dis)); for(i=1;i<=k;i++) scanf("%d",&id[i]); for(i=1;i<=k;i++) dis[1<<i-1][id[i]]=0; for(i=1;i<=m;i++) { scanf("%d%d%lld",&x,&y,&z);add(x,y,z);add(y,x,z); } for(j=1;j<=mask;j++){ for(i=1;i<=n;i++){ for(p=j&(j-1);p;p=j&(p-1)){ dis[j][i]=min(dis[j][i],dis[p][i]+dis[j-p][i]); } } for(i=1;i<=n;i++){ q.push(make_pair(-dis[j][i],i)); } while(!q.empty()){ x=q.top().second;q.pop(); if(vis[j][x])continue; vis[j][x]=1; for(i=head[x];i;i=nxt[i]){ if(dis[j][to[i]]>dis[j][x]+val[i]){ dis[j][to[i]]=dis[j][x]+val[i]; q.push(make_pair(-dis[j][to[i]],to[i])); } } } } LL ans=1ll<<60; for(i=1;i<=n;i++)ans=min(ans,dis[mask][i]); printf("%lld\n",ans); }