这样阴沉的天气持续下去,我们不免担心起他的健康。
51nod魔法学校近日开展了主题为“天气晴朗”的魔法交流活动。
N名魔法师按阵法站好,之后选取N - 1条魔法链将所有魔法师的魔力连接起来,形成一个魔法阵。
魔法链是做法成功与否的关键。每一条魔法链都有一个魔力值V,魔法最终的效果取决于阵中所有魔法链的魔力值的和。
由于逆天改命的魔法过于暴力,所以我们要求阵中的魔法链的魔力值最大值尽可能的小,与此同时,魔力值之和要尽可能的大。
现在给定魔法师人数N,魔法链数目M。求此魔法阵的最大效果。
Input
两个正整数N,M。(1 <= N <= 10^5, N <= M <= 2 * 10^5) 接下来M行,每一行有三个整数A, B, V。(1 <= A, B <= N, INT_MIN <= V <= INT_MAX) 保证输入数据合法。
Output
输出一个正整数R,表示符合条件的魔法阵的魔力值之和。
Input示例
4 6 1 2 3 1 3 1 1 4 7 2 3 4 2 4 5 3 4 6
Output示例
12
Wizmann
(题目提供者)
蜜汁最小生成树。。。幸好搜到了大神博客(膜拜三分钟先。。。)下面我来解释一下思路:
首先看了题意是求最小生成树,但是呢,它不是一般的最小生成树(或许大概可能应该叫最大生成树),这棵树有两个条件:1.最大的边尽可能的小 2.树的权值尽可能的大,什么意思呢?就是说我先找一棵权值最小的生成树——这时候它最大的边一定是最小的(因为构造最小生成树的时候每一条边都是选择符合条件的最小的),这时候我们就把它最大的边记录下来,然后试着把它其余的边换成权值比较大的边,这样就可以满足两个条件了。
下面是代码部分↓↓↓
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define ll long long
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
//边的集合
struct Edge{
int start;
int end;
int value;
} edge[ MAXN*2 ];
//记录根节点
int vis[MAXN];
//从小到大排序以找到最小的最大边
bool cmpMin( Edge aa , Edge bb ){
return aa.value<bb.value;
}
//从大到小排序以找到边不超过某个值的情况下最大生成树
bool cmpMax( Edge aa , Edge bb ){
return aa.value>bb.value;
}
//寻找根节点
int find( int x ){
int r = x;
while( r!=vis[r] )
r = vis[r];
int i=x,j;
//路径压缩
while( i!=r ){
j = i;
vis[i] = r;
i = vis[j];
}
return r;
}
//传入图的顶点数和边数,计算最小生成树的最大边
ll kruskalMinSumValue( int n , int m ){
for( int i=1 ; i<=n ; i++ )
vis[i] = i;
sort( edge,edge+m,cmpMin );
int max = 0; //记录最大边
int cnt = 0; //记录已添加的边数
for( int i=0 ; i<m ; i++ ){
int dx = find( edge[i].start );
int dy = find( edge[i].end );
if( dx != dy ){
//记录最大边
if( max < edge[i].value )
max = edge[i].value;
vis[dx] = dy;
cnt++;
}
//边数够了,最小生成树构造完成
if( cnt == n-1 )
return max;
}
return -1;
}
//传入图的顶点数和边数以及限定的最大边的值
ll kruskalMaxSumValue( int n , int m , int maxValue ){
for( int i=1 ; i<=n ; i++ )
vis[i] = i;
sort( edge,edge+m,cmpMax );
ll sum = 0;
int cnt = 0;
for( int i=0 ; i<m ; i++ ){
//舍去大于最大值的边
if( edge[i].value>maxValue )
continue;
int dx = find( edge[i].start );
int dy = find( edge[i].end );
if( dx != dy ){
vis[dx] = dy;
cnt++;
//记录最大生成树的权
sum += edge[i].value;
}
//最大生成树构造完成
if( cnt == n-1 )
return sum;
}
return -1;
}
int main(){
int n,m;
scanf( "%d%d",&n,&m );
int s,e,v;
for( int i=0 ; i<m ; i++ ){
scanf( "%d%d%d",&edge[i].start,&edge[i].end,&edge[i].value );
}
//取得最大边的最小值
ll maxValue = kruskalMinSumValue( n,m );
//取得限定边情况下最大生成树的权
ll maxSumValue = kruskalMaxSumValue( n,m,maxValue );
printf( "%lld\n",maxSumValue );
}
That's all .