You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 1143. 联络员

一、题目描述

Tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvj网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员 两两都可以联络(直接或者是间接都可以)。本题中所涉及的通信渠道都是 双向 的。

Tyvj是一个公益性的网站,没有过多的利润,所以你要 尽可能的使费用少 才可以。

目前你已经知道,Tyvj的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。

数据保证给出的通信渠道可以让所有的管理员联通

注意: 对于某两个管理员 u,v,他们之间可能存在多条通信渠道,你的程序应该累加所有 u,v 之间的必选通行渠道。

输入格式 第一行两个整数 nm 表示Tyvj一共有 n 个管理员,有 m 个通信渠道;

第二行到 m+1 行,每行四个非负整数,p,u,v,wp=1 时,表示这个通信渠道为必选通信渠道;当 p=2 时,表示这个通信渠道为选择性通信渠道;u,v,w 表示本条信息描述的是 uv 管理员之间的通信渠道,u 可以收到 v 的信息,v 也可以收到 u 的信息,w 表示费用。

输出格式 一个整数,表示最小的通信费用

数据范围 1≤n≤2000,1≤m≤10000

输入样例

5 6
1 1 2 1
1 2 3 1
1 3 4 1
1 4 1 1
2 2 5 10
2 2 5 5

输出样例

9

二、题目解析

本题给出的图中有两类边,第一类边是 必选 的,连上这些边之后,我们还要加上若干个第二类边,使得所有点连通,要求花费最小,属于kruskal算法的简单应用。

我们只需要在读取边的时候遇见第一类边就加入到集合中去,读完所有边后,对第二类边排序,然后自小到大选择第二类边,如果这条边上的两个顶点还不连通,就将连接这条边。由于第一类边在读取时已经处理完成了,所以不需要存储,只存储第二类边即可,这样可以节省排序和并查集的插入的时间。

#include <bits/stdc++.h>
using namespace std;
const int N = 2010, M = 10010;
int n, m, p[N];

struct Edge {
    int a, b, w;
    bool operator<(const Edge &t) const {
        return w < t.w;
    }
} e[M];
int el;

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

int res;

int main() {
    cin >> n >> m;

    // 初始化并查集
    for (int i = 1; i <= n; i++) p[i] = i;

    for (int i = 0; i < m; i++) {
        int a, b, c, t;
        cin >> t >> a >> b >> c;
        if (t == 1) // 必选的加入到同一个并查集
            p[find(a)] = find(b), res += c;
        else
            // 记录可选边有哪些
            e[el++] = {a, b, c};
    }
    // 对可选边进行由小到大排序
    sort(e, e + el);

    // 枚举每条可选边
    for (int i = 0; i < el; i++) {
        int a = find(e[i].a), b = find(e[i].b), c = e[i].w;
        if (a != b) p[a] = b, res += c;
    }

    // 输出最短长度
    printf("%d\n", res);
    return 0;
}