为什么感觉状压dp都好玄学……FlashHu大佬太强啦……
设$f_{i,j}$表示当前选的点集为$i$,下一次要加入的点集为$j$时,新加入的点和原有的点之间的最小边权。具体的转移可以枚举$i$,然后枚举$i$的补集$j$,找出$j$的$lowbit_j$
那么转移就是$$f_{i,j}=min{f_{i,j-lowbit_j}+min{dis[lowbit_j][i]}}$$
据说这一部分的复杂度是$O(3^nn)$,因为$n$元素的所有子集的大小之和是$3^n$(然而我并不会证)
然后考虑把整张图给分层,设$g_{l,i}$表示总层数为$l$,已选点集为$i$时的最小答案,然后转移就是
$$g_{l,i}=sum_{jin i}g_{l-1,i-j}+l*f_{i-j,j}$$
据说这个复杂度也是$O(3^nn)$,最后$max{g_{l,2^n-1}}$就是答案了
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 6 char buf[1<<21],*p1=buf,*p2=buf; 7 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} 8 inline int read(){ 9 #define num ch-'0' 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc())) 12 (ch=='-')&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return res; 17 } 18 const int N=13,M=4096,inf=0x01010101; 19 int a[N][N],f[M][M],g[N][M],ne[M],lg[M]; 20 int n,m,S,s,l,x,y,v; 21 int main(){ 22 // freopen("testdata.in","r",stdin); 23 memset(a,1,sizeof(a)); 24 memset(g,63,sizeof(g)); 25 n=read(),m=read(); 26 S=(1<<n)-1; 27 for(int i=0;i<n;++i) lg[1<<i]=i; 28 while(m--){ 29 x=read(),y=read(),v=read(); 30 if(a[--x][--y]>v) a[x][y]=a[y][x]=v; 31 } 32 for(int i=1;i<=S;++i){ 33 v=0; 34 for(int j=s=S^i;j;j=(j-1)&s) 35 ne[j]=v,v=j; 36 //枚举i的补集,同时为了保证转移正确性要反着转移才行 37 for(int j=v;j;j=ne[j]){ 38 x=lg[j&-j],v=inf; 39 for(y=0;y<n;++y) 40 if(1<<y&i) cmin(v,a[x][y]); 41 f[i][j]=f[i][j^(j&-j)]+v; 42 } 43 } 44 for(int i=1;i<=S;i<<=1) g[0][i]=0; 45 for(int l=1;l<n;++l) 46 for(int i=1;i<=S;++i) 47 for(int j=i;j;j=(j-1)&i) 48 cmin(g[l][i],g[l-1][i^j]+f[i^j][j]*l); 49 int v=0x3f3f3f3f; 50 for(int l=0;l<=n;++l) cmin(v,g[l][S]); 51 printf("%d ",v); 52 return 0; 53 }