#include using namespace std; const int INF = 0x01010101; // 这个上限有点意思,需要仔细思考 const int N = 15; // 点的个数上限  int g[N][N]; // 记录边,就是地图 int n, m; // n个节点,m条边 vector p[N]; // 第一维:哪一行;第二维:此行第几个是哪个点 bool st[N]; // 每个点是否被用过 int res = INF; // 结果 /* * u:第u层 * k:正在枚举第几号节点,尝试将这个节点填充到u层中去 * cnt:已经使用了cnt个点 * sum:目前生成树最小价值 */ void dfs(int u, int k, int cnt, int sum) { if (sum >= res) return; // 走一半就比最优值大,剪枝 if (p[u - 1].size() == 0) return; // 上一层没有选入点(上层为空),树无法继续构建,递归出口 if (cnt == n) { // 所有点都已经被构建到树中,结果有资格参选最优解 res = sum; // 因为上面有最优剪枝,大的都被提前返回了,到达这里时肯定是更小的值 return; } if (k > n) { // 该层所有点都讨论过一遍,继续下一层,回到1号节点处开始 dfs(u + 1, 1, cnt, sum); return; } // 如果u层,k号点被标识为用过了 if (st[k]) { // 该点已用,继续讨论下一个节点 dfs(u, k + 1, cnt, sum); return; } int cost = INF; // 找到该点与上层的最小联系 for (int i = 0; i < p[u - 1].size(); i++) // 若没有联系(即与上层所有点不连通),则mini是INF,会被最优性剪枝减去 cost = min(cost, g[p[u - 1][i]][k]); // 上一行的每个节点,如果与k号节点的边相连,并且,权值最小的话,更新cost // 找到了上一层到u层,使得k点点亮的最小代价值 // 选用此点(可能1) st[k] = 1; // 标识k点已使用 p[u].push_back(k); dfs(u, k + 1, cnt + 1, sum + cost * u); p[u].pop_back(); st[k] = 0; // 回溯 // 不用此点(可能2) dfs(u, k + 1, cnt, sum); } int main() { memset(g, 0x3f, sizeof g); // 数组初始化 scanf("%d %d", &n, &m); while (m--) { int a, b, c; scanf("%d %d %d", &a, &b, &c); g[a][b] = g[b][a] = min(g[a][b], c); // 无向图 } for (int i = 1; i <= n; i++) { // 每个点都当一次根 st[i] = 1; // i点已用 p[0].push_back(i); // 根是第0层,第1个放的是i dfs(1, 1, 1, 0); // 从第一层,第一个节点,已经使用了一个节点(根),现在的代价是0 p[0].pop_back(); // 回溯 st[i] = 0; // 回溯 } cout << res << endl; return 0; }