51nod 1640 天氣晴朗的魔法 (圖論,並查集)

 

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;
}

 

 

 

 

 

 

發佈了46 篇原創文章 · 獲贊 6 · 訪問量 7391
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章