次小生成树: 利用prim算法求出一棵MST,再将不在MST中的边进行枚举,进行边的添加的时候一定会形成环,我们需要将这环中除了刚刚加入的那条边以外的权值最大的那条边删除,这样得到的可能是一棵比较小的树,因此只要找到树中两点之间的最大边权并保存下来,再进行其他所有不在树中的边的枚举即可。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
#define maxn 1010
#define MS(a, b) memset(a, b, sizeof(a));
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int map[maxn][maxn];
int dis[maxn];
int mmax[maxn][maxn];
bool used[maxn][maxn];
bool vis[maxn];
int pre[maxn];
int n, m;
int Prim() {
int ans = 0;
MS(mmax, 0);
MS(used, false);
MS(vis, false);
for (int i = 1; i <= n; i++) {
dis[i] = map[1][i];
if (i >= 2) {
pre[i] = 1;
}
}
pre[1] = 0;
vis[1] = true;
for (int ii = 2; ii <= n; ii ++) {
int min_dis = inf;
int pos;
for (int i = 1; i <= n; i++) {
if (!vis[i] && min_dis > dis[i]) {
min_dis = dis[i];
pos = i;
}
}
if (min_dis == inf) {
return -1;
}
else {
cout << " ads " << min_dis << endl;
ans += min_dis;
vis[pos] = true;
used[pos][pre[pos]] = used[pre[pos]][pos] = true;
// 记录树中两点之间的边
for (int i = 1; i <= n; i++) {
if (vis[i]) {
mmax[i][pos] = mmax[pos][i] = max(mmax[i][pre[pos]], dis[pos]);
//i到pos 的最大边权 就是i到pos的前驱中最大边权和pos前驱到pos的距离中的最大值
}
else if (!vis[i] && dis[i] > map[pos][i]) {
dis[i] = map[pos][i];
pre[i] = pos;
}
}
}
}
return ans;
}
int Sec_mst(int tot) {
int ans = inf;
for (int i = 1; i <= n; i ++) { // 枚举所有不在MST中的边 并去掉两点之间的最大值进行比较
for (int j = i + 1; j <= n; j ++) {
if (map[i][j] != inf && !used[i][j]) {
ans = min(ans, tot + map[i][j] - mmax[i][j]);
}
}
}
if (ans == inf) {
return -1;
}
else return ans;
}
int main() {
int T;
cin >> T;
while(T--) {
cin >> n >> m;
MS(map,inf);
for (int i = 1; i <= n; i ++) {
map[i][i] = 0;
}
for (int i = 0; i < m; i ++) {
int a, b, c;
cin >> a >> b >> c;
map[a][b] = map[b][a] = c;
}
int ans = Prim();
cout << ans << " asdasd" << endl;
if (ans == -1) { //如果不联通,不存在次小生成树
cout << "Not Unique" << endl;
}
else if (ans == Sec_mst(ans)){ // 如果MST == 次小 则不唯一
cout << "Not Unique" << endl;
}
else { //MST唯一
cout << ans << endl;
}
}
}