本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

正解:斯坦纳树
解题报告:
斯坦纳树的略微加强版——斯坦纳森林。
考虑有可能最优方案是一个森林,我们先求出斯坦纳树的f数组之后,再用g[s]表示连通状态至少为s的最小代价,然后枚举一个子集,将若干个斯坦纳树合并,注意只能合并合法的状态!
也就是前k位和后k位1的个数相等的状态!
最后,不要像我一样忘记判no solution…
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXN = 52;
const int MAXM = 2017;
const int MAXS = 1024;
int n,m,k,ecnt,f[MAXN][MAXS],S,head,tail,dui[MAXM*100];
int first[MAXN],to[MAXM],Next[MAXM],w[MAXM],g[MAXS];
bool vis[MAXN];
inline void link(int x,int y,int z){ Next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; }
inline int calc(int s){ int tot=0; while(s) tot+=s&1,s>>=1; return tot; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}
inline void SPFA(int s){
head=tail=0; for(int i=1;i<=n;i++) if(f[i][s]!=f[0][0]) dui[++tail]=i,vis[i]=1;
int u;
while(head<tail) {
head++; u=dui[head]; vis[u]=0;
for(int i=first[u];i;i=Next[i]) {
int v=to[i];
if(f[v][s]>f[u][s]+w[i]) {
f[v][s]=f[u][s]+w[i];
if(!vis[v]) { vis[v]=1; dui[++tail]=v; }
}
}
}
}
inline void work(){
int T=getint(); int x,y,z,now;
while(T--) {
n=getint(); m=getint(); k=getint();
ecnt=0; memset(first,0,sizeof(first));
for(int i=1;i<=m;i++) {
x=getint(); y=getint(); z=getint();
link(x,y,z); link(y,x,z);
}
memset(f,0x3f,sizeof(f)); S=(1<<(k*2))-1;
for(int i=1;i<=k;i++) f[i][(1<<(i-1))]=0;
for(int i=n-k+1;i<=n;i++) f[i][(1<<(i+k*2-n-1))]=0;
for(int s=1;s<=S;s++) {
for(int i=1;i<=n;i++) {
for(int from=(s-1)&s;from;from=(from-1)&s) {
if(f[i][from]==f[0][0] || f[i][s-from]==f[0][0]) continue;
now=f[i][from]+f[i][s-from];
if(f[i][s]>now) f[i][s]=now;
}
}
SPFA(s);
}
memset(g,0x3f,sizeof(g)); int ss=(1<<k)-1;
//可能是森林,考虑将几棵树组合
for(int s=1;s<=S;s++) {
if(calc(s&ss)!=calc(s>>k)) continue;
for(int i=1;i<=n;i++)
g[s]=min(g[s],f[i][s]);
}
for(int s=1;s<=S;s++) {
for(int from=(s-1)&s;from;from=(from-1)&s) {
if(g[from]==g[0] || g[s-from]==g[0]) continue;
g[s]=min(g[s],g[from]+g[s-from]);
}
}
if(g[S]==g[0]) puts("No solution");
else printf("%d\n",g[S]);
}
}
int main()
{
work();
return 0;
}
原文:http://www.cnblogs.com/ljh2000-jump/p/6418474.html