整理的算法模板:ACM算法模板總結(分類詳細版)
給定一棵N個節點的樹,要求增加若干條邊,把這棵樹擴充爲完全圖,並滿足圖的唯一最小生成樹仍然是這棵樹。
求增加的邊的權值總和最小是多少。
注意: 樹中的所有邊權均爲整數,且新加的所有邊權也必須爲整數。
輸入格式
第一行包含整數t,表示共有t組測試數據。
對於每組測試數據,第一行包含整數N。
接下來N-1行,每行三個整數X,Y,Z,表示X節點與Y節點之間存在一條邊,長度爲Z。
輸出格式
每組數據輸出一個整數,表示權值總和最小值。
每個結果佔一行。
數據範圍
1≤N≤60001≤N≤6000
1≤Z≤1001≤Z≤100
輸入樣例:
2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5
輸出樣例:
4
17
思路:
首先呢,題目初始給的樹一定是最小生成樹;然後要想變成完全圖之後的最小生成樹仍然是原來的樹,那麼每次加入連通塊的邊一定要剛好比連通塊內的邊的最大值大1;
具體含義就是,在剛開始用kruskal算法建立最小生成樹的時候,就開始加邊;每次對於兩個連通塊 s[a] 和 s[b] ,如果合併兩個連通塊之後要變成完全圖需要加上邊的個數爲 連通塊a的大小乘上連通塊b的大小減去1 : s[a] * s[b] -1;(減去1是減去一條本身連通兩個連通塊的邊);
那麼合併之後加上的邊的權值的大小要大於整個連通塊的邊的最大值;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=6005;
int s[N],p[N];
struct node
{
int a,b,w;
}e[N];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(p[x]!=x)
{
p[x]=find(p[x]);
}
return p[x];
}
int main()
{
int t;
cin >>t;
while(t--)
{
int n;
cin >>n;
for(int i=0;i<=n;i++) s[i]=1,p[i]=i;
for(int i=0;i<n-1;i++)
{
int a,b,w;
cin >>a>>b>>w;
e[i]={a,b,w};
}
sort(e,e+n-1,cmp);
int ans=0;
for(int i=0;i<n-1;i++)
{
int a=find(e[i].a),b=find(e[i].b);
ans+=(s[a]*s[b]-1)*(e[i].w+1);
s[a]+=s[b];
p[b]=a;
}
cout <<ans<<endl;
}
}