[atARC078F]Mole and Abandoned Mine
注意到最终图的样子可以看作一条从1到$n$的路径,以及删去这条路径上的边后,路径上的每一个点所对应的一个连通块
考虑dp,令$f_{S,i}$表示当前1到$n$路径上的最后一个点以及之前点(包括$i$)所对应连通块的并,转移考虑枚举下一个点以及其对应的连通块,即$f_{S\cup T,j}=\min(f_{S,i}+sum(S,T)-len(i,j))$
(其中$len(i,j)$表示$(i,j)$这条边的长度,$sum(S,T)=\sum_{x\in S,y\in T,(x,y)\in E}len(x,y)$)
初始状态为$f_{S,1}=0$(其中$1\in S$且$S$的导出子图连通),$f_{other}=\infty$
转移条件为$(i,j)\in E$、$j\in T$、$S\cap T=\empty$且$T$的导出子图连通,因此转移复杂度为$o(n^{2}3^{n})$(关于$T$导出子图连通的这个条件预处理即可)
进一步优化,关于$j$和$T$的枚举可以分开,即先求出$g_{j}=\min(f_{S,i}-len(i,j))$,再枚举包含$j$的$T$即可(这样做的实际意义是先将$S$中所有到$j$的边选择最小的),时间复杂度降为$o(n3^{n})$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 15 4 struct ji{ 5 int nex,to,len; 6 }edge[N*N]; 7 int E,n,m,x,y,z,head[N],vis[N],g[N],con[1<<N],sum[1<<N],f[1<<N][N]; 8 void add(int x,int y,int z){ 9 edge[E].nex=head[x]; 10 edge[E].to=y; 11 edge[E].len=z; 12 head[x]=E++; 13 } 14 void dfs(int k,int s){ 15 if (((s&(1<<k))==0)||(vis[k]))return; 16 vis[k]=1; 17 for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to,s); 18 } 19 int main(){ 20 scanf("%d%d",&n,&m); 21 memset(head,-1,sizeof(head)); 22 for(int i=1;i<=m;i++){ 23 scanf("%d%d%d",&x,&y,&z); 24 add(x-1,y-1,z); 25 add(y-1,x-1,z); 26 } 27 memset(f,0x3f,sizeof(f)); 28 for(int i=0;i<(1<<n);i++){ 29 memset(vis,0,sizeof(vis)); 30 for(int j=0;j<n;j++) 31 if (i&(1<<j)){ 32 dfs(j,i); 33 break; 34 } 35 bool flag=0; 36 for(int j=0;j<n;j++) 37 if ((i&(1<<j))&&(!vis[j])){ 38 flag=1; 39 break; 40 } 41 if (!flag){ 42 con[i]=1; 43 if (i&1)f[i][0]=0; 44 } 45 } 46 for(int i=1;i<(1<<n);i+=2){ 47 if (!con[i])continue; 48 for(int x=0;x<n;x++){ 49 g[x]=0x3f3f3f3f; 50 sum[(1<<x)]=0; 51 } 52 for(int x=0;x<n;x++) 53 if ((i&(1<<x))){ 54 for(int j=head[x];j!=-1;j=edge[j].nex){ 55 y=edge[j].to; 56 if ((i&(1<<y))==0){ 57 g[y]=min(g[y],f[i][x]-edge[j].len); 58 sum[(1<<y)]+=edge[j].len; 59 } 60 } 61 } 62 int ii=(1<<n)-1-i; 63 for(int j=(ii&(ii-1));j>=0;j=((j-1)&ii)){ 64 int jj=(ii^j),k=(jj&(jj-1)); 65 sum[jj]=sum[jj^k]+sum[k]; 66 if (jj==ii)break; 67 } 68 for(int x=0;x<n;x++) 69 for(int j=ii;j;j=((j-1)&ii)) 70 if ((j&(1<<x))&&(con[j]))f[i|j][x]=min(f[i|j][x],g[x]+sum[j]); 71 } 72 printf("%d",f[(1<<n)-1][n-1]); 73 }