题意:
n个点,每个点有一个权值记作node[i],在第i个点和第j个点之间连无向边的代价为node[i]+node[j],有m条额外边可选择,第u个点和第v个点连无向边的代价为w,额外边可以用可以不用。问连边使任意两点可连通的总代价是多少。
数据范围:1 <= n <= 2e5 , 0 <= m<= 2e5。
题解:
1.kruskal算法求最小生成树模板题。
2.找到权值最小的点t,将除t之外的点和t相连是原有的边,与m条额外边共同组成可选的边集合E。
3.n <= 2e5 ,因为边是无向的且包括原有的边和额外边,故总边数cnt <= 8e5,但远远小于2e5 * 2e5。这个是稀疏图,应该使用kruskal算法。
4.n个顶点m条边的最小生成树,kruskal复杂度O(m*logm)适合稀疏图,prim复杂度O(n²)适合稠密图。
#include<bits/stdc++.h>
#define N 800005
using namespace std ;
int n , m , cnt = 0 ;
long long node[N] ;
int pre[N] ;
struct Edge
{
int u , v ;
long long weight ;
} edge[N] ;
int t ;
int find(int x)
{
if(x == pre[x])
return pre[x] ;
pre[x] = find(pre[x]) ;
return pre[x] ;
}
bool cmp(Edge a , Edge b)
{
return a.weight < b.weight ;
}
void kruskal()
{
int i , j ;
int u , v ;
int ru , rv ;
long long sum = 0 ;
sort(edge , edge + cnt , cmp) ;
for(i = 0 ; i < cnt ; i ++)
{
u = edge[i].u ;
v = edge[i].v ;
ru = find(u) ;
rv = find(v) ;
if(ru == rv)
continue ;
pre[ru] = rv ;
sum += edge[i].weight ;
}
printf("%lld" , sum) ;
}
int main()
{
int i , j ;
int u , v ;
Edge a ;
long long min1 = 1e18 ;
scanf("%d%d" , &n , &m) ;
for(i = 1 ; i <= n ; i ++)
{
scanf("%lld" , &node[i]) ;
if(node[i] < min1)
{
min1 = node[i] ;
t = i ;
}
pre[i] = i ;
}
for(i = 1 ; i <= n ; i ++)
{
if(i == t)
continue ;
a.u = t ;
a.v = i ;
a.weight = node[t] + node[i] ;
edge[cnt ++] = a ;
a.u = i ;
a.v = t ;
edge[cnt ++] = a ;
}
for(i = 1 ; i <= m ; i ++)
{
scanf("%d%d%lld" , &u , &v , &a.weight) ;
a.u = u ;
a.v = v ;
edge[cnt ++] = a ;
a.u = v ;
a.v = u ;
edge[cnt ++] = a ;
}
kruskal() ;
}