不能和其他題目重名的最小生成樹
題目
知識點:最小生成樹的Kruskal 算法
已知一個完全圖唯一的最小生成樹(即知道這個樹所有邊的端點和權值),其餘的邊權值未知,問這個完全圖所有邊權值和的最小值。
完全圖是每對頂點之間都恰連有一條邊的簡單圖。
輸入
第一行一個整數t表示數據組數(1≤t≤10)
每組數據第一行一個正整數n,表示完全圖的點數(2≤n≤105)
接下來n-1行,每行三個整數x,y,z,表示x,y之間有一條權值爲z的邊(無向邊) (1≤x,y≤n,1≤z≤10000)
輸出
每組數據一行一個整數
輸入
2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5
輸出
9
20
思路
比較難,需要對kruskal算法有比較好的理解。
考慮kruskal算法的過程,掃描到邊(x,y,z)時,x所在的集合爲Sx,y所在的集合爲Sy,此時需要合併兩個集合,並添加一條邊,形成一顆新樹。爲了保證(x,y,z)一定在最後的最小生成樹中,那麼∀u∈Sx,∀v∈Sy,(u,v)≠(x,y),uv邊的權值應該是大於z的。爲了是圖最後的邊權值最小,uv的邊權值應該爲z+1
代碼
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
struct edg
{
int x, y, z;
bool operator<(const edg& r) const
{
return z < r.z;
}
};
const int ms = 1e5 + 10;
int pre[ms], s[ms];
edg e[ms];
int find(int x)
{
if (pre[x] != x)
pre[x] = find(pre[x]);
return pre[x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
int n; ll res = 0;
cin >> n;
for (int i = 1; i < n; ++i)
{
cin >> e[i].x >> e[i].y >> e[i].z;
res += e[i].z;
}
for (int i = 1; i <= n; ++i)
{
pre[i] = i; s[i] = 1;
}
sort(e + 1, e + n);
for (int i = 1; i < n; ++i)
{
int fx = find(e[i].x), fy = find(e[i].y);
if (fx != fy)
{
pre[fx] = fy;
res += 1ll * (e[i].z + 1)*(s[fx] * s[fy] - 1);
s[fy] += s[fx];
s[fx] = 0;
}
}
cout << res << "\n";
}
return 0;
}