|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
const int INF = 0x01010101; // 这个上限有点意思,需要仔细思考
|
|
|
const int N = 15; // 点的个数上限
|
|
|
int g[N][N]; // 记录边,就是地图
|
|
|
int n, m; // n个节点,m条边
|
|
|
vector<int> 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;
|
|
|
} |