题解:小于n/2的点用steiner tree算法,大于n/2的点用最小生成树。
#include"bits/stdc++.h"
using namespace std;
const int N = 30;
const int inf = 0x3f3f3f3f;
int n,m;
int head[N], sign[N], vis[N];
int f[N][1<<14],st[N],d[1<<14],fa[N];
struct edge{
int nxt,v,w;
edge(){}
edge(int nxt, int v, int w):nxt(nxt),v(v),w(w){}
}E[N*N];
queue<int> q;
struct node{
int u,v,w;
node(){}
node(int u, int v, int w) : u(u), v(v), w(w){}
bool operator < (const node &a) const{
return w < a.w;
}
}p[N*N];
int tail;
inline void init()
{
tail = 0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
memset(st,0,sizeof(st));
}
inline void add(int u, int v, int w)
{
E[tail] = edge(head[u],v,w); head[u] = tail++;
}
void spfa(int sta)
{
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = head[u]; ~i; i = E[i].nxt){
int v = E[i].v, w = E[i].w;
if(f[v][sta] > f[u][sta] + w){
f[v][sta] = f[u][sta] + w;
if(!vis[v]){
vis[v] = 1;
q.push(v);
}
}
}
vis[u] = 0;
}
}
//预处理出st数组点的集合和f数组,不同的题目有所不同
void prepare(int S, int k)
{
for(int i = 1; i <= n; i++)
for(int j = 0; j < S; j++)
f[i][j] = inf;
for(int i = 1; i <= k; i++)
st[i] = 1<<(i-1), f[i][st[i]] = 0;
}
void stenier(int S, int k)
{
prepare(S,k);
for(int sta = 0; sta < S; sta++){
for(int i = 1; i <= n; i++){
for(int s = sta; s; s = (s-1)&sta){
f[i][sta] = min(f[i][sta],f[i][sta^s]+f[i][s]);
}
if(f[i][sta] != inf) q.push(i), vis[i] = 1;
}
spfa(sta);
}
}
inline int get_fa(int x)
{
return fa[x] == x? x : fa[x] = get_fa(fa[x]);
}
int kruskal()
{
int ans = 0;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
int u = p[i].u, v = p[i].v, w = p[i].w;
if(sign[u] || sign[v]) continue;
int fu = get_fa(u), fv = get_fa(v);
if(fu == fv) continue;
fa[fu] = fv;
ans += w;
}
for(int i = 1; i <= n; i++) if(sign[i] == 0){
if(get_fa(i) != get_fa(1)) return inf;
}
return ans;
}
void solve2(int nt)
{
for(int i = 1; i <= n/2; i++)
sign[i] = 0;
for(int i = 1; i < (1<<nt); i++) {
for(int j = 0; j < nt; j++) {
if(i&(1<<j)) sign[j+1+n/2] = 0;
else sign[j+1+n/2] = 1;
}
d[i] = kruskal();
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif // LOCAL
while(~scanf("%d%d",&n,&m)){
init();
for(int i = 1,u,v,w; i <= m; i++) {
scanf("%d%d%d",&u,&v,&w);
p[i] = node(u,v,w);
add(u,v,w); add(v,u,w);
}
sort(p+1,p+1+m);
int S = 1<<(n/2), k = n/2, nt = n-n/2;
stenier(S,k);
solve2(nt);
int x = 1;
for(int i = 2; i <= n/2; i++) {
x = (x<<1) + 1;
int ans = inf;
for(int j = 1; j <= n; j++)
ans = min(ans,f[j][x]);
printf("%d\n",ans);
}
x = 0;
for(int i = 1; i <= nt; i++) {
x = (x<<1) + 1;
int ans = inf;
for(int j = x; j < (1<<nt); j++)
if((j&x) == x){
ans = min(ans,d[j]);
}
printf("%d\n",ans);
}
}
return 0;
}