问题 E: 魔法交流活动
题目描述
魔法学校近日开展了主题为“天气晴朗”的魔法交流活动。
N名魔法师按阵法站好,之后选取N - 1条魔法链将所有魔法师的魔力连接起来,形成一个魔法阵。
魔法链是做法成功与否的关键。每一条魔法链都有一个魔力值V,魔法最终的效果取决于阵中所有魔法链的魔力值的和。
由于逆天改命的魔法过于暴力,所以我们要求阵中的魔法链的魔力值最大值尽可能的小,与此同时,魔力值之和要尽可能的大。
现在给定魔法师人数N,魔法链数目M。求此魔法阵的最大效果。
输入
两个正整数N,M。(1 <= N <= 10^5, N <= M <= 2 * 10^5)
接下来M行,每一行有三个整数A, B, V。(1 <= A, B <= N, INT_MIN <= V <= INT_MAX)
保证输入数据合法。
输出
输出一个正整数R,表示符合条件的魔法阵的魔力值之和。
样例输入
4 6
1 2 3
1 3 1
1 4 7
2 3 4
2 4 5
3 4 6
样例输出
12
-
大致题意分析:
有n个点,选取n-1条边把他们全部连起来形成一棵树,每条边都有一个权值;要求1:所有的边的权值的最大值最小,然后还又要求2:这棵树中的所有的边权值的和最大。
思路:这个题目是对边来进行筛选的,需要用克鲁斯卡尔蒜法。这个蒜法是对边来进行操作的,按照边来逐渐形成整棵最小生成树。
具体需要跑两边,第一遍从小到大对边进行一次筛选,筛选出的边的集合可以构成一颗树即可;这是最后一条新加进来的边就是最大的权值maxmin(要求1达成)。
第二遍,从大到小,具体从等于maxmin的边开始跑一遍,直到筛选出的边的集合可以构成一颗树即可,进行筛选、求和,即为最终结果。
代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <iostream> 4 #include <algorithm> 5 #include <queue> 6 #include <stack> 7 #include <vector> 8 #include<math.h> 9 #include <string.h> 10 #include<set> 11 using namespace std; 12 #define inf 0x7fffffff 13 #define maxn 10000000 14 const double pi=acos(-1.0); 15 #define ll long long 16 #define N 100008 17 //vector<int>G[N]; 18 int n,m; 19 struct Edge{ 20 int a,b;//两个顶点的编号 21 ll dis; 22 Edge(int a=0,int b=0,ll dis=0):a(a),b(b),dis(dis){} 23 }edge[N*2]; 24 int root[N]; 25 ll fans; 26 27 void init(int n){ 28 29 for(int i=1;i<=n;i++) 30 root[i]=i; 31 } 32 33 34 int findroot(int a){//返回点a的根节点 35 if(root[a]==a) 36 return a; 37 return root[a]=findroot(root[a]); 38 } 39 int unite(int a,int b){//将a和b节点用并查集的思路连接到一起 40 if(a==b)return 0;//在联通块内部加上的边 41 else{ 42 root[a]=b;return 1; 43 } 44 } 45 bool cmp(Edge x,Edge y){ 46 return x.dis<y.dis; 47 } 48 int fact1(int n){//计算出maxlmin-len 49 int cnt=0; 50 ll ans=(ll)inf*(-1); 51 int i=1; 52 while(cnt<n-1){ 53 if(unite(findroot(edge[i].a),findroot(edge[i].b))==1 ){ 54 cnt++; 55 ans=max(ans,edge[i].dis); 56 } 57 i++; 58 } 59 return ans; 60 } 61 ll fact2(int n,int maxlen){// 62 for(int i=1;i<=n;i++) 63 root[i]=i; 64 int cnt=0; 65 ll ans=0; 66 int i=m; 67 while(i>0&&cnt<n-1){ 68 if(edge[i].dis<=(ll)maxlen && unite(findroot(edge[i].a),findroot(edge[i].b))==1 ){ 69 cnt++; 70 ans+=edge[i].dis; 71 } 72 i--; 73 } 74 return ans; 75 } 76 int main(){ 77 while(scanf("%d%d",&n,&m)!=EOF){ 78 init(n); 79 int x,y;ll v; 80 for(int i=1;i<=m;i++){ 81 scanf("%d%d%lld",&x,&y,&v); 82 edge[i]=Edge(x,y,v); 83 // G[x].push_back(y);G[x].push_back(v); 84 // G[y].push_back(x);G[y].push_back(v); 85 } 86 sort(edge+1,edge+1+m,cmp);//按边权值升序排列 87 int maxlen=fact1(n);//找到合法的最大长度 88 89 // printf("maxmin=%d\n",maxlen); 90 // vis[1]=true; 91 92 printf("%lld\n",fact2(n,maxlen)); 93 } 94 95 return 0; 96 }