题解 | #Forsaken喜欢独一无二的树#

Forsaken喜欢独一无二的树

https://ac.nowcoder.com/acm/problem/53074

题意

删除一些边,使得最小生成树唯一,问删除的边的权值和最小是多少?

思路

为什么最小生成树不唯一:因为连接相同两个集合的边,权值相同的可能有多个
所以,我们只需要删除 权重相同的 且 连接同两个集合的 那些边即可 :这些边中留一个 用于维持最小生成树,其余的均删掉。

因为是 稀疏图,所以使用 Kruskal , 在里面进行操作即可

关于在 Krustral 中 res 先加 w 后来再 减 w 的解释:
加:为了保证最小生成树唯一,假设权值相同的这一波边都必须删除
为什么 a == b 的不删: a == b 说明在前几波 权重相同的边中,a 所在集合 和 b 所在集合 已经连接,此时这一波边的权值和前面出现过的几波权值 肯定不同,所以该边留下 不会影响最小生成树的唯一性,故不删

减:减去的是 维持最小生成树所必须的边

Code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=2e5+10;

struct node{
    int a,b,w;
}edges[N];
int p[N];
int n,m;

bool cmp(node x,node y){
    return x.w<y.w;
}

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

ll Kruskal_change(){
    ll res=0;
    for(int i=0;i<m;) {
        int j=i;
        for(;j<m;j++) if(edges[i].w!=edges[j].w) break;  // 求 j 

        for(int id=i;id<j;id++){  // 遍历这一波 权值相同的边
            int a=edges[id].a,b=edges[id].b,w=edges[id].w;
            a=find(a),b=find(b);
            if(a!=b) res+=w;  //在此处 a==b 的说明,之前已经被连过
        }

        for(int id=i;id<j;id++){
            int a=edges[id].a,b=edges[id].b,w=edges[id].w;
            a=find(a),b=find(b);
            if(a!=b)   res-=w,p[a]=b;
        }
        // res先加w,再减去w的解释:
        i=j;
    }
    return res;
}

int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b,w;
        cin>>a>>b>>w;
        edges[i]={a,b,w};
    }

    sort(edges,edges+m,cmp);
    for(int i=1;i<=n;i++) p[i]=i;

    ll t=Kruskal_change();
    cout<<t<<endl;

    return 0;
}
全部评论

相关推荐

蚂蚁 基架java (n+6)*16 签字费若干
点赞 评论 收藏
分享
11-08 17:36
诺瓦科技_HR
点赞 评论 收藏
分享
无敌虾孝子:喜欢爸爸还是喜欢妈妈
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务