题目传送门:https://www.luogu.org/problemnew/show/P3959
题意:给出一个有$N$个点的图,求其中的一个生成树(指定一个点为根),使得$\sum\limits_{i=1}^{N-1} v_i \times dep_i$最小,其中$v_i$为生成树上某条边的边权,$dep_i$为这条边连接的两个点中深度较浅的点的深度。$N \leq 12 , v \leq 5 \times 10^5$
$N \leq 12$给我们一个很强烈的信息:状态压缩
(所以这题还可以用暴力枚举+可持久化并查集拿90pts,用%你退火和诡异贪心拿到满分)
我们不妨考虑一层一层加入点
设$g_{i,j}$表示点集$j$向点集$i$每个点连一条边的最小权值和,可以在$O(3^NN^2)$的复杂度内算出(似乎可以优化成$O(3^NN)$)
然后设$f_{i,j}$表示现在的生成树中有$i$层节点,其中深度最深的节点集合为$j$时的最小权值,考虑转移为$f_{i+1,k}=f_{i,j}+g_{j,k}$
最后答案为$min\{f_{k,(1<<N)-1}\}$
复杂度为$O(3^NN^2)$,实际可优化成$O(3^NN)$
1 // luogu-judger-enable-o2
2 #include<bits/stdc++.h>
3 #define MAXN (1 << 12) + 1
4 using namespace std;
5
6 inline int read(){
7 int a = 0;
8 char c = getchar();
9 while(!isdigit(c))
10 c = getchar();
11 while(isdigit(c)){
12 a = (a << 3) + (a << 1) + (c ^ '0');
13 c = getchar();
14 }
15 return a;
16 }
17
18 inline int min(int a , int b){
19 return a < b ? a : b;
20 }
21
22 int f[MAXN][MAXN] , g[13][MAXN] , route[13][13];
23
24 int main(){
25 memset(route , 0x3f , sizeof(route));
26 memset(g , 0x3f , sizeof(g));
27 int N = read() , M = read();
28 if(N == 1){
29 cout << 0;
30 return 0;
31 }
32 for(int i = 0 ; i < N ; i++)
33 g[0][1 << i] = 0;
34 while(M--){
35 int a = read() , b = read() , c = read();
36 route[a][b] = route[b][a] = min(route[a][b] , c);
37 }
38 for(int i = 1 ; i < 1 << N ; i++){
39 int s = ((1 << N) - 1) ^ i;
40 for(int j = s ; j ; j = j - 1 & s){
41 int p = j;
42 for(int k = p & -p ; p ; k = p & -p){
43 int minN = 0x3f3f3f3f;
44 p -= k;
45 int t = log2(k) + 1 , q = i;
46 for(int m = q & -q ; q ; m = q & -q){
47 q -= m;
48 minN = min(minN , route[t][(int)log2(m) + 1]);
49 }
50 if(minN == 0x3f3f3f3f){
51 f[i][j] = 0x3f3f3f3f;
52 break;
53 }
54 f[i][j] += minN;
55 }
56 }
57 }
58 for(int i = 1 ; i < N ; i++)
59 for(int j = 1 ; j < 1 << N ; j++){
60 if(j == (j & -j))
61 continue;
62 for(int k = j - 1 & j ; k ; k = k - 1 & j)
63 if(f[j ^ k][k] != 0x3f3f3f3f && g[i - 1][j ^ k] != 0x3f3f3f3f)
64 g[i][j] = min(g[i][j] , g[i - 1][j ^ k] + f[j ^ k][k] * i);
65 }
66 int all = 0x7fffffff;
67 for(int i = 1 ; i < N ; i++)
68 all = min(all , g[i][(1 << N) - 1]);
69 cout << all;
70 return 0;
71 }