题目链接
https://www.luogu.org/problemnew/show/P3959
分析
首先最后得到的一定是一颗生成树,从树高为h的节点x向深处扩展y的代价为(h+1) * dis(x,y)
我们以树高作为阶段,枚举已经扩展哪些节点的二进制状态x,又向深处扩展哪些节点的二进制状态y,设$f[dep][sta]$为已经挖到第dep层,当前已经挖了的宝藏用二进制表示下的状态为sta时的最小代价。状态转移方程为
$f[dep+1][x|y] = min(f[dep+1][x|y] , f[dep][x] + (dep+1) * cost(x,y))$,$y$满足$y$是$x$补集的子集
$cost(x,y)$是集合$x$扩展到集合$y$的最小距离,可以先通过计算点到集合的距离,再计算集合到集合的距离
这时候就有一个不怎么naive的问题,这个$x$到$y$的距离不一定就是$x$中最优情况下最深那一层,也就是dep层上的节点,而是可能是由其他层数上的节点连过来的,这样不又是不合法的转移吗?
这时候JYH大佬又跳了出来,说道:当前你这么不合法的枚举肯定比合法枚举不知道低到哪里去了(因为 * (dep+1)).而且层数是递增枚举的,枚举每一层时所有状态又会被枚举一遍,所以说你这个不是由最深层上的点扩展的状态一定在之前被枚举过,所以这么做是资瓷的
菜鸡RyeCatcher于是又开始%起了JYH这个OI又强还有妹子的巨佬
JYH巨佬很高兴又教了他几招:
1 2 3
| for(ri s=sta;s;s=(s-1)&sta)
s= sta^U
|
还教会他时间复杂度:
我们枚举了所有状态的补集的子集,相当于枚举了所有自己的子集
空集的子集只有一个 $C^0_N $
一个元素的子集有$C^1_N$个,这个又有$2^1$个子集
两个元素的子集有$C^2_N$个,这个又有$2^2$个子集
…
所有子集的子集个数就有$C^0_N \times 1 + C^1_N \times 2^1 + C^2_N \times 2^2 + … + C^N_N \times 2^N$
二项式定理可知这不就是$(2+1)^N$,加上钦定根节点和枚举层数,时间复杂度$O(3^N N^2)$
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <queue> #define ll long long #define ri register int using std::min; using std::max; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c=='-'; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x;return ; } const int maxn=12; const ll inf=1e17+19260817; ll f[1<<12][13]; ll ss_dis[1<<12][1<<12],ps_dis[1<<12][13],g[13][13]; int n,m; int U,C; int main(){ int x,y;ll z; ll ans=inf; read(n),read(m); U=1<<n; for(ri i=0;i<U;i++){ for(ri j=0;j<U;j++)ss_dis[i][j]=inf; for(ri j=0;j<n;j++)ps_dis[i][j]=f[i][j]=inf; } for(ri i=0;i<n;i++){ for(ri j=0;j<n;j++)if(i!=j)g[i][j]=inf; } for(ri i=0;i<m;i++){ read(x),read(y),read(z); x--,y--; g[x][y]=g[y][x]=min(g[x][y],z); } for(ri i=0;i<n;i++){ for(ri j=0;j<U;j++){ for(ri k=0;k<n;k++){ if(j&(1<<k)){ ps_dis[j][i]=min(ps_dis[j][i],g[i][k]); } } } } for(ri i=0;i<U;i++){ C=i^(U-1); for(ri j=C;j;j=(j-1)&C){ z=0; for(ri k=0;k<n;k++){ if(j&(1<<k)){ if(dis[i][k]==inf)z=inf; else z+=ps_dis[i][k]; } } ss_dis[j][i]= (z>=inf)? inf:z; } } for(ri r=0;r<n;r++){ for(ri i=0;i<U;i++){ for(ri j=0;j<n;j++)f[i][j]=inf; } f[1<<r][0]=0; for(ri d=0;d<n;d++){ for(ri i=0;i<U;i++){ C=i^(U-1); if(f[i][d]==inf)continue; for(ri j=C;j;j=(j-1)&C){ if(f[i][d]==inf)continue; f[i|j][d+1]=min(f[i|j][d+1],f[i][d]+1ll*(d+1)*ss_dis[j][i]); } } } for(ri d=0;d<n;d++)ans=min(ans,f[U-1][d]); } printf("%lld\n",ans); return 0; }
|