【CCF 201412-4】最优灌溉 (求最小生成树)

CCF 201412-4 最优灌溉                                                                       

问题描述

  雷雷承包了很多片麦田,为了灌溉这些麦田,雷雷在第一个麦田挖了一口很深的水井,所有的麦田都从这口井来引水灌溉。
  为了灌溉,雷雷需要建立一些水渠,以连接水井和麦田,雷雷也可以利用部分麦田作为“中转站”,利用水渠连接不同的麦田,这样只要一片麦田能被灌溉,则与其连接的麦田也能被灌溉。
  现在雷雷知道哪些麦田之间可以建设水渠和建设每个水渠所需要的费用(注意不是所有麦田之间都可以建立水渠)。请问灌溉所有麦田最少需要多少费用来修建水渠。

输入格式

  输入的第一行包含两个正整数n, m,分别表示麦田的片数和雷雷可以建立的水渠的数量。麦田使用1, 2, 3, ……依次标号。
  接下来m行,每行包含三个整数ai, bi, ci,表示第ai片麦田与第bi片麦田之间可以建立一条水渠,所需要的费用为ci。

输出格式

  输出一行,包含一个整数,表示灌溉所有麦田所需要的最小费用。

样例输入

4 4
1 2 1
2 3 4
2 4 2
3 4 3

样例输出

6

样例说明

  建立以下三条水渠:麦田1与麦田2、麦田2与麦田4、麦田4与麦田3。

评测用例规模与约定

  前20%的评测用例满足:n≤5。
  前40%的评测用例满足:n≤20。
  前60%的评测用例满足:n≤100。
  所有评测用例都满足:1≤n≤1000,1≤m≤100,000,1≤ci≤10,000。

  【题意】

   “灌溉所有麦田所需要的最小费用”即求解该图的最小生成树的边长之和

 【类型】
  Kruskal算法+贪心法
 【分析】
  Kruskal算法,模板题

 【注意】

 1、初次做的时候最小生成树的边长之和,设置为double类型,然而求出的ans的值一直不对,提交了还是0分,百思不得其解

       后来看了别人的博客->点击打开链接,尝试把double全部改成了long long类型,后来AC得了100分

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>

using namespace std;
const int nmax=1000+10;
const int mmax=1000000+10;

struct  Edge{
    int  u,v;
    long long dist;
    Edge(){}
    Edge(int u,int v, long long d):u(u),v(v),dist(d){}
    bool operator<(const Edge&rhs)const{
        return dist<rhs.dist;
    }
};
struct Kruskal{
    int n,m;//无向图的点数n和边数m
    //Edge edges[mmax];    //定义静态数组的边列表
    vector<Edge> edges;//定义动态数组的边列表

    int fa[nmax];
    int findset(int x){
        return fa[x]==-1?x:fa[x]=findset(fa[x]);
    }
    void init(int n){
        this->n=n;
        m=0;
        memset(fa,-1, sizeof(fa));
    }
    void AddEdge(int u,int v, long long dist){
        //edges[m++]=Edge(u,v,dist);
        //如若定义动态数组
        edges.push_back(Edge(u,v,dist));
        m=edges.size();
    }
    long long kruskal(){
        long long sum=0;//最小生成树的边长之和
        int cnt=0;
        //sort(edges,edges+m);
        sort(edges.begin(),edges.end());

        for(int i=0;i<m;i++) {
            int u = edges[i].u;//得到第i条边的起点
            int v = edges[i].v;//得到第i条边的终点
            if(findset(u)!=findset(v)){//如果这两个点不在同一个连通分量
                sum+=edges[i].dist;//同步更新生成树的边长之和
                fa[findset(u)]=findset(v);//合并这两个连通分量
                if(++cnt>=n-1){//如果图中仅剩一个连通分量,则退出
                    break;
                }
            }
        }
        if(cnt<n-1) return -1;//当最小生成树不存在时,返回-1
        return sum;//如若最小生成树存在,返回边长之和

    }
}KK;
int main() {
    int n,m;//无向图的点数n和边数m
    while(scanf("%d%d",&n,&m)==2){

        KK.init(n);
        for(int i=0;i<m;i++){
            int u,v;
            long long d;
            //cin>>u>>v>>d;//输入变得起点、终点、边长
            scanf("%d%d%d",&u,&v,&d);
            u--;v--;
            KK.AddEdge(u,v,d);//向图中增加一条边
            //KK.AddEdge(v,u,d); //本题目中,两点之间有向边和无向边的效果是相同的
        }
        long long ans=KK.kruskal();
        printf("%lld",ans);
        //if(ans==-1 || ans>sum_max) printf("Not enough cable\n");
        //else printf("Need %.1f miles of cable\n",ans);
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值