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

来源:互联网 发布:红警2尤复简体优化版 编辑:程序博客网 时间:2024/06/05 20:53

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 61 2 31 3 11 4 72 3 42 4 53 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;}



原创粉丝点击