题解 | #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;
}
字节跳动公司福利 1311人发布