最小生成树算法 大鱼海棠

问题 G: 大鱼海棠

时间限制: 1 Sec  内存限制: 128 MB
提交: 29  解决: 12
[提交][状态][讨论版]

题目描述

近期热映的电影《大鱼海棠》灵感来源于《庄子·逍遥游》,讲述了一个掌管海棠花的少女与人类男孩“鲲”的灵魂的奇幻故事。
所有人类的灵魂都是海里一条巨大的鱼,出生的时候从海的此岸出发,在路途中,有时相遇,有时分开,死的时候去到海的彼岸,之后变成一条沉睡的小鱼,等待多年后的再次出发,这个旅程永远不会结束,生命往复不息。十六岁生日那天,居住在“神之围楼”里的一个名叫椿的女孩变作一条海豚到人间巡礼,被大海中的一张网困住,一个人类男孩因为救她而落入深海死去。为了报恩,为了让人类男孩复活,她需要在自己的世界里,历经种种困难与阻碍,帮助死后男孩的灵魂——一条拇指那么大的小鱼,成长为一条比鲸更巨大的鱼并回归大海。
然后……我去看了这部电影……电影中主要人物有三个——椿,鲲,湫。在椿生活的世界里,鲲是一条鱼。
这个世界中有n个城市。编号1至n。椿,鲲,湫居住在城市1。
鲲在这个世界里行走,是需要通过“水道”的!而在椿的世界里,平时并不会出现“水道”。
某一天,鲲长大了,即将回到人类世界!椿想和鲲最后一次一起在自己的世界里游历所有的城市。因此,椿向湫寻求帮助,湫施展了魔法,变出了m条水道供椿选择:每条水道连接了两个城市u,v,以及椿建造这条水道需要消耗的法力值w。
椿可以选择其中的n-1条水道进行建造,但要保证能游历所有的城市,请问椿最少需要消耗多少法力值?

 

如果椿无论怎么建造水道都无法保证和鲲游历完所有的城市,请输出-1。

输入

多组测试数据。

每组测试数据,第一行输入n,m。

n表示有n个城市,m表示椿可以在这m条水道中进行选择。(数据中 n最大为100,m最大为10000)

接下来m行,每行输入三个数u,v,w。表示可以在城市u和城市v之间建立一条水道,建造该便水道消耗的法力值为w。(w最大为200.)

数据中,两个城市之间可能多条有费用不一样的水道可供选择。

输出

如果无论椿怎么建造水道都不能和鲲游历完所有的城市,请输出-1。

否则,请输出椿建造水道的需要消耗的最少法力值。

样例输入

2 1
1 2 3
3 1
1 2 3

样例输出

3
-1


#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
int pre[105];
struct node
{
    int u,v,w;
    friend bool operator< (node a,node b)
    {
        return a.w>b.w;
    }
};
int find(int x)//查找他们的根
{
    int r=x;
    while(pre[r]!=r) r=pre[r];//找根的根,直到找到原根
    return r;
}
int main()
{
    priority_queue<node>q;//用优先队列,使每次取出的w总是最小的
    int n,m;
    node a[10005];
    while(cin>>n>>m)
    {
        while(!q.empty()) q.pop();
        int i;
        for(i=1;i<=m;i++)
        {
            cin>>a[i].u>>a[i].v>>a[i].w;
            pre[i]=i;//用于存放各个点的跟
            q.push(a[i]);
        }
        int k=0;
        int s=0;//存费用(权)
        for(i=1;i<=n-1;i++)//n个点至少需要n-1条边来连通
        {
            while(!q.empty())
            {
                node x;
                x=q.top();q.pop();
                int yv=find(x.v);
                int yu=find(x.u);
                if(yv==yu) continue;//如果两个点本来就连通,跳过
                pre[yu]=yv;//让一个点的根成为另一个点的子根
                s=s+x.w;
                k++;//记录已连接的边数
            }
        }
        if(k!=n-1) cout<<-1<<endl;//小于n-1代表没有连通
        else cout<<s<<endl;
    }
    return 0;
}
                

    并查集实际上还可以压缩,这个以后再说,我还没学。

  主要的思路就是,把u,v,w存入优先队列,优先队列中w小的在前(优先队列不懂的话可以看我博客中优先队列之一类里的题,我已分类好了),一个个取出,如果取出的两个点已经连通,就取下一个,否则“并”(让一个点的根成为另一个点的子根)。



posted on 2018-01-22 17:10  蔡军帅  阅读(216)  评论(0编辑  收藏  举报