[atARC078F]Mole and Abandoned Mine

 注意到最终图的样子可以看作一条从1到$n$的路径,以及删去这条路径上的边后,路径上的每一个点所对应的一个连通块

考虑dp,令$f_{S,i}$表示当前1到$n$路径上的最后一个点以及之前点(包括$i$)所对应连通块的并,转移考虑枚举下一个点以及其对应的连通块,即$f_{Scup T,j}=min(f_{S,i}+sum(S,T)-len(i,j))$

(其中$len(i,j)$表示$(i,j)$这条边的长度,$sum(S,T)=sum_{xin S,yin T,(x,y)in E}len(x,y)$)

初始状态为$f_{S,1}=0$(其中$1in S$且$S$的导出子图连通),$f_{other}=infty$

转移条件为$(i,j)in E$、$jin T$、$Scap 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 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14313468.html