题目大意 给你$n$个点,$m$条边,要你选一个点作为根建一棵生成树满足代价最小
一棵生成树的代价是$\Sigma \; dep[i] * dis[fa_i][i]$, 其中$dep_i$表示$i$节点在这棵生成树中的深度(根节点深度为$0$,$dis[fa_i][i]$表示$i$节点到他父亲节点的距离
题目解析 首先通过$n\leq12$可以发现这是一道状压dp/搜索题
这里我们考虑状压dp
我们设状态$f[i]$表示选点的状态为$i$时,这棵生成树的最小代价,$st_{i,j}$表示当选点状态为$i$且$i$状态取最优方案时节点$j$的深度
那么我们可以很快想到一个dp方程
初始值满足$f_{2^s}=0,st_{2^s,s}=0$,其余位置的$f$和$st$都为$\mathrm{inf}$
这个方程的复杂度是$O(n^2 \times 2^n)$的
注意到根不是固定的, 所以我们可以每次选定一个根来进行dp的计算,总复杂度$O(n^3\times2^n)$
代码实现 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 #include <bits/stdc++.h> #define R register #define ll long long #define sum(a, b, mod) (((a) + (b)) % mod) const int MaxN = 13 ;int n, m;int g[MaxN][MaxN], st[1 << MaxN][MaxN], f[1 << MaxN];int dp (int s) { memset (st, 0x3f , sizeof (st)), memset (f, 0x3f , sizeof (f)); int lim = (1 << n); f[1 << s] = 0 , st[1 << s][s] = 0 ; for (int i = 1 ; i < lim; i++) { if (f[i] < 0x3f3f3f3f ) { for (int j = 0 ; j < n; j++) { if (i & (1 << j)) { for (int k = 0 ; k < n; k++) { if (!(i & (1 << k))) { if ((g[j][k] != 0x3f3f3f3f ) && (f[i | (1 << k)] > f[i] + (g[j][k] * (st[i][j] + 1 )))) { f[i | (1 << k)] = f[i] + (g[j][k] * (st[i][j] + 1 )); memcpy (st[i | (1 << k)], st[i], sizeof (st[i | (1 << k)])); st[i | (1 << k)][k] = st[i][j] + 1 ; } } } } } } } return f[lim - 1 ]; } int main () { scanf ("%d%d" , &n, &m); memset (g, 0x3f , sizeof (g)); for (int i = 1 ; i <= m; i++) { int u, v, d; scanf ("%d%d%d" , &u, &v, &d), --u, --v; g[u][v] = std ::min(g[u][v], d), g[v][u] = std ::min(g[v][u], d); } int ans = 0x3f3f3f3f ; for (int i = 0 ; i < n; i++) ans = std ::min(ans, dp(i)); printf ("%d\n" , ans); return 0 ; }