算法训练 ——安慰奶牛

问题描述
Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。

道路被用来连接 N 个牧场,牧场被连续地编号为 1 到 N,每一个牧场都是一个奶牛的家。

FJ 计划除去 P 条道路中尽可能多的道路,但是还要保持牧场之间的连通性。

你首先要决定那些道路是需要保留的 N - 1 条道路。

第 j 条双向道路连接了牧场 Sj 和 Ej (Sj != Ej),而且走完它需要 Lj 的时间,没有两个牧场是被一条以上的道路所连接。

奶牛们非常伤心,因为她们的交通系统被削减了,你需要到每一个奶牛的住处去安慰她们。

每次你到达第 i 个牧场的时候(即使你已经到过),你必须花去 Ci 的时间和奶牛交谈。

你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。

在早上起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次,这样你才能完成你的交谈任务。

假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。

输入格式
第 1 行包含两个整数 N 和 P。
接下来 N 行,每行包含一个整数 Ci
接下来 P 行,每行包含三个整数 Sj, Ej 和 Lj

输出格式
输出一个整数, 所需要的总时间 (包含和在你所在的牧场的奶牛的两次谈话时间)。

样例输入
5 6
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6

样例输出(官网样例是错的)
178

数据范围
5 ≤ N ≤ 10000,N-1 ≤ P ≤ 100000,0 ≤ Lj ≤ 1000,1 ≤ Ci ≤ 1,000。


题解
Kruscal:

题意简述:求一棵最小生成树,并选择一个起点,遍历整棵树,最后回到起点的最小花费是多少。

解题思路

  1. 假设有两个点,那么路径可以是 1 —> 2 —> 1,可以发现每条边被遍历了两次,起点和终点分别被遍历了两次与一次,

  2. 所以我们可以令 边权 = 边权 * 2 + c[u] + c[v],最后 答案 = 最小生成树 + 起点 (点权最小的点)

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 10010, M = 100010;

struct edge
{
    int a, b, w;
}e[M];

int c[N], p[N];

bool cmp(edge a, edge b)
{
    return a.w < b.w;
}

int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int n, m;
    cin >> n >> m;
    
    int MIN = 1000;
    for (int i = 1; i <= n; i ++) 
    {
        cin >> c[i];
        MIN = min(MIN, c[i]);
    }
    
    for (int i = 0; i < m; i ++)
    {
        int a, b, w;
        cin >> a >> b >> w;
        e[i] = {a, b, w * 2 + c[a] + c[b]};
    }
    
    sort(e, e + m, cmp);
    
    for (int i = 1; i <= n; i ++) p[i] = i;
     
    int sum = 0, cnt = 0;
    for (int i = 0; i < m; i ++)
    {
        int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
        if(a != b)
        {
            cnt ++;
            p[a] = b;
            sum += w;
        }
        
        if(cnt == n - 1) break;
    }
    
    cout << sum + MIN << endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值