<题目链接>
题目大意:
给定一张$n$个顶点(每个顶点有点权)的无向图,并且给出边权为$w_i$的m条边,顶点$u$和顶点$v$直接如果建边,边权为$a_u + a_v$,求图连通的最小边权和。
解题分析:
可以发现,如果仅仅只是考虑根据点权连边的话,那么最优的情况就是所有点(除点权最小的点)与点权最小的点相连。如果加上题目给出的边权综合考虑,我们就是将这m+n-1条边加入考虑的范围内,然后根据这些边建一颗最小生成树。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 #define N int(2e5+7) 6 #define rep(i,s,t) for(int i=s;i<=t;i++) 7 int n,m,pre[N]; 8 ll val[N]; 9 10 struct Edge{ 11 int fi,se; 12 ll cost; 13 Edge(int _fi=0,int _se=0,ll _cost=0):fi(_fi),se(_se),cost(_cost){} 14 bool operator<(const Edge & tmp)const { 15 return cost<tmp.cost; 16 } 17 }edge[N<<1]; //最多只用考虑m+n-1条边 18 19 vector<Edge>vec; 20 21 int find(int x){ return pre[x]==x?x:pre[x]=find(pre[x]); } 22 23 void Kruskal(){ 24 ll sum=0,cnt=0; 25 sort(vec.begin(),vec.end()); 26 for(int i=0;i<vec.size();i++){ 27 Edge now=vec[i]; 28 int f1=find(now.fi),f2=find(now.se); 29 if(f1!=f2){ 30 pre[f1]=f2; 31 sum+=now.cost; 32 cnt++; 33 } 34 if(cnt>=n-1)break; 35 } 36 printf("%lld ",sum); 37 } 38 39 int main(){ 40 scanf("%d%d",&n,&m); 41 ll mn=1e18;int mnloc=-1; 42 rep(i,1,n){ 43 scanf("%lld",&val[i]); 44 pre[i]=i; 45 if(val[i]<mn)mn=val[i],mnloc=i; //记录最小的节点序号和权值 46 } 47 rep(i,1,m){ 48 int u,v;ll w;scanf("%d%d%lld",&u,&v,&w); 49 vec.push_back(Edge(u,v,w)); //真实的边权 50 } 51 rep(i,1,n){ 52 if(i==mnloc)continue; 53 vec.push_back(Edge(i,mnloc,val[i]+mn)); //点间连边的点权之和 54 } 55 Kruskal(); 56 }
2019-02-21