【ACWing】1141. 局域网

这是一篇关于利用Kruskal算法解决局域网回路问题的博客。题目要求在保持网络连通性的前提下,删除一些边以消除回路,同时使删除边的总重量最大化。通过Kruskal算法,可以找到每个连通块的最小生成树,从而达到目标。文章详细介绍了Kruskal算法的原理和步骤,并给出了C++代码实现,时间复杂度为O(mlogm),空间复杂度为O(n)。
摘要由CSDN通过智能技术生成

题目地址:

https://www.acwing.com/problem/content/1143/

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

注意:对于某一个连接,虽然它是双向的,但我们不将其当做回路。本题中所描述的回路至少要包含两条不同的连接。两台计算机之间最多只会存在一条连接。不存在一条连接,它所连接的两端是同一台计算机。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f ( i , j ) f(i,j) f(i,j)表示 i , j i,j i,j之间连接的畅通程度, f ( i , j ) f(i,j) f(i,j)值越小表示 i , j i,j i,j之间连接越通畅。现在我们需要解决回路问题,我们将除去一些连线,使得网络中没有回路且不影响连通性(即如果之前某两个点是连通的,去完之后也必须是连通的),并且被除去网线的 ∑ f ( i , j ) \sum f(i,j) f(i,j)最大,请求出这个最大值。

输入格式:
第一行两个正整数 n , k n,k n,k。接下来的 k k k行每行三个正整数 i , j , m i,j,m i,j,m表示 i , j i,j i,j两台计算机之间有网线联通,通畅程度为 m m m

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

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

问题本质是要求一些边,使得删掉它们之后,剩余的连通块依然不变,并且每个连通块是原连通块的最小生成树。要返回的是删掉的边的总长度。思路是Kruskal算法。在求一个连通图的最小生成树的时候,Kruskal算法的流程是这样的:
1、先将所有的边按照边权从小到大排序;
2、依次尝试加入每个边,如果加边之前两个端点已经连通了,那么就不加这条边,否则要加;
3、遍历完之后就找到了一棵最小生成树(对于连通图来说,最小生成树是一定存在的)。

关于Kruskal算法的正确性。如果某条边 x → y x\to y xy在Kruskal算法里要加,说明此时 x x x y y y各自的连通块是不连通的。如果不加,那么产生的最小生成树里, x x x y y y也一定连通,这样连同 x → y x\to y xy这条边,就产生了一个环。这个环里一定存在某个长度大于等于 x → y x\to y xy这条边的边(否则的话在加 x → y x\to y xy这条边之前 x x x y y y就已经连通了,Kruskal算法里会略过这条边),将这条边替换为 x → y x\to y xy这条边,就得到了含 x → y x\to y xy的最小生成树。说明Kruskal算法每次加的边都可以存在于某个最小生成树中,所以算法正确。这里其实还需要证明算法一定会加恰好 n − 1 n-1 n1条边。这也是显然的。首先一定不会少加,否则的话由原图连通性,一定存在某条边该加的时候没加;其次也不会多加,当恰好加到 n − 1 n-1 n1条边之后,所有点已经连通了,后面的边都会略过。

这道题并不会求得一个最小生成树,因为一开始未必所有点都在同一个连通块里。但是这里,按照Kruskal算法的方式来做仍然是正确的,因为在每个连通块单独看来,其实就相当于在这个小连通块上做Kruskal算法。代码如下:

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

const int N = 110, M = 210;
int n, m;

struct Edge {
    int a, b, w;
    bool operator<(const Edge &t) const {
        return w < t.w;
    }
} e[M];
// 这里的p数组是并查集的parent数组
int p[N];

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

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, w;
        cin >> a >> b >> w;
        e[i] = {a, b, w};
    }

	// 按照边权从小到大排序
    sort(e, e + m);

    int res = 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) p[a] = b;
        else res += w;
    }

    cout << res << endl;

    return 0;
}

时间复杂度 O ( m log ⁡ m ) O(m\log m) O(mlogm) m m m是边的数量,空间 O ( n ) O(n) O(n)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值