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.

4.4 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 1141 局域网

一、题目描述

某个局域网内有 n 台计算机和 k双向 网线,计算机的编号是 1n。由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。

注意:

对于某一个连接,虽然它是双向的,但我们不将其当做回路。本题中所描述的回路至少要包含两条不同的连接。

两台计算机之间最多只会存在一条连接。(无重边)

不存在一条连接,它所连接的两端是同一台计算机。(无环)

因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f(i,j) 表示 i,j 之间连接的畅通程度,f(i,j)越小 表示 i,j 之间连接 越通畅

现在我们需要解决回路问题,我们将除去一些连线,使得网络中 没有回路不影响连通性(即如果之前某两个点是连通的,去完之后也必须是连通的),并且被除去网线的 \sum f(i,j) 最大,请求出这个 最大值

输入格式 第一行两个正整数 n,k

接下来的 k 行每行三个正整数 i,j,m 表示 i,j 两台计算机之间有网线联通,通畅程度为 m

输出格式 一个正整数,表示被除去网线的 \sum f(i,j) 的最大值。

数据范围 1≤n≤100 ,0≤k≤200,1≤f(i,j)≤1000

输入样例

5 5
1 2 8
1 3 1
1 5 3
2 4 5
3 4 2

输出样例

8

二、Kruskal算法

本题要求 被除去网线的通畅程度之和最大,则要求 留下来的网线通畅程度最小,也就是求图的 最小生成树 由于原图 不一定是连通图,所以要求的实际上是原图的 最小生成森林,即若干个生成树的集合。

kruskal算法是 求连通块 的,所以这个题直接用 kruskal 很容易求出来。

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

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

//并查集
int find(int x) {
    if (fa[x] != x) fa[x] = find(fa[x]); //路径压缩
    return fa[x];
}
int main() {
    cin >> n >> m;

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

    // Kruskal算法直接记录结构体
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        e[i] = {a, b, c};
    }
    sort(e, e + m); //不要忘记e数组的长度是边的数量

    int res = 0;
    //枚举每条边
    for (int i = 0; i < m; i++) {
        int a = find(e[i].a), b = find(e[i].b), c = e[i].w;
        if (a != b)
            fa[a] = b;
        else
            res += c; //去掉的边权
    }
    printf("%d\n", res);
    return 0;
}

三、Prim算法

既然题目要求的可能是多个连通块,如果非得用Prim算法的话,是不是得先求出连通块,然后对每个连通块,求出其最小生成树,这样才是最小生成森林呢?

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

const int N = 110;
int w[N][N];
int dist[N];
bool st[N];
int n, m, sum;
int b[N];//桶记录哪些点已经处理过了找出未处理过的进行一下Flood Fill

int prim(int source) {
    memset(dist, 0x3f, sizeof dist);
    dist[source] = 0;
    b[source] = 1;

    int res = 0;
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        st[t] = true;

        if (dist[t] != INF) res += dist[t], b[t] = 1;
        for (int j = 1; j <= n; j++)
            dist[j] = min(dist[j], w[t][j]);
    }
    return res;
}

int main() {
    cin >> n >> m;
    memset(w, 0x3f, sizeof w);
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        w[a][b] = w[b][a] = c;
        sum += c; // 总边长
    }

    int s = 0;
    for (int i = 1; i <= n; i++)
        if (!b[i]) s += prim(i);

    printf("%d\n", sum - s);
    return 0;
}