51nod 1640 天气晴朗的魔法 (图论,并查集)

43 篇文章 0 订阅

 

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

  题面明确,构成一个连接连接所有点的树.其中最大的边尽可能小,在这个前提下,其他边尽量大.

 

 1: 最大的边尽可能小,那么可以用kurskal算法或者prim算法算法他的最小生成树,记录最大的那条边。

 2: 然后就知道,就算只选取小于这条边的边,也至少能构成1棵树(最小生成树),根据题目要求,优先选择大的边(但要小于1中记录的最大值)

 3: 很明显这里用kurskal更加的方便,因为优先选择大的边,在一开始构成最小生成树时已经排序好,记录最大边的位置,从尾到头重新遍历即可。

 4:注意如果有边和最大边一样长,也要记录,还有就是sort语句的自定义排序语句,情况为'=='时不能返回1,今天还特意问人查资料,具体原因自行百度补一波更清楚。

并查集不会的话,可以看我上一篇博客有我点一点理解。

我推荐一个视频:http://www.bilibili.com/video/av8373130/  

这是某大佬的教学,讲的很好,吃个饭一边看5分钟,吃完就会并查集了。

然后 献上代码:

 

#include <iostream>
#include <cstdio>
#include <string.h>
#include <fstream>
#include <algorithm>
using namespace std;
    long long int ans=0;
    int fu[100005],i,j,n,m,gen1,gen2,maxbian=0,maxbnum;
struct bi
{
    int from,to,leng;
}bian[200005];
int cmp1(bi a,bi b)  //小的边在前面
{
        return a.leng<b.leng;
}
int cmp2(bi a,bi b)   //大的在前面
{
    return b.leng>a.leng;
}
int FindGen(int x)  //路径压缩   查找
{
    int a,b;
    a=x;//保存初始值
    while(fu[x]!=x)//找到根节点,而不是父节点
        x=fu[x];    //得到根点x
                      //下面开始路径压缩
    while(fu[a]!=x)//不是根的直接子点
    {
         b=fu[a];//保存父点,方便更改
         fu[a]=x;
         a=b;
    }
    return x;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)   //父节点数组初始化
        fu[i]=i;
    for(i=0;i<m;i++)
        scanf("%d%d%d",&bian[i].from,&bian[i].to,&bian[i].leng);
    sort(bian,bian+m,cmp1);
    for(i=0;i<m;i++)
    {
        gen1=FindGen(bian[i].from);   //分别两个点的根点
         gen2=FindGen(bian[i].to);
         if(gen1!=gen2)    //根不同
         {
             fu[gen1]=gen2; //并合
             if(bian[i].leng>maxbian)
             {
                maxbian=bian[i].leng;
                maxbnum=i;
             }
         }
         if(bian[i].leng==maxbian&&maxbnum!=i)//可能最大边不止一条
            maxbnum=i;
    }
    for(i=1;i<=n;i++)   //父节点数组初始化
        fu[i]=i;
    for(i=maxbnum;i>=0;i--)
    {
        gen1=FindGen(bian[i].from);   //分别两个点的根点
         gen2=FindGen(bian[i].to);
         if(gen1!=gen2)    //根不同
         {
             ans+=bian[i].leng;
             fu[gen1]=gen2; //并合
         }
    }
   printf("%lld",ans);
   return 0;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值