题339.最小生成树扩展-acwing-Q1145–北极通讯网络
一、题目
二、题解
本题给定k个卫星要你利用这些无视距离的卫星通信去去掉将所有点连通所形成的最小生成树里的较大的边,从而使得剩下的连通块中的最大边的权值最小(而不是单纯去掉最小生成树中的k-1条较大边,因为它是给点成边,受限制),输出这个结果。
由于最小生成树Kruskal算法利用了并查集,每次做并操作可让连通块个数少1,所以显然依据前面的分析,当连通块个数减少到k(这k个连通块可用k-1条卫星通信边连接)时,之前所选的边的权值即为答案。代码如下:
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int maxn=550;
struct Edge
{
int u,v;
double w;
bool operator < (const Edge &e) const
{
return w<e.w;
}
};
int n,k;
pii p[maxn];
vector<Edge> e;
double res;
int fa[maxn];
double getD(pii p1,pii p2)//求两点间距离,这里为边权
{
int dx=p1.x-p2.x,dy=p1.y-p2.y;
return sqrt(dx*dx+dy*dy);
}
int findRoot(int v)//找根
{
if(fa[v]<0)
{
return v;
}
else
{
return fa[v]=findRoot(fa[v]);
}
}
void unionSet(int v1,int v2)//并集
{
int root1=findRoot(v1),root2=findRoot(v2);
if(root1==root2)
{
return;
}
if(fa[root1]<fa[root2])
{
fa[root1]+=fa[root2];
fa[root2]=root1;
}
else
{
fa[root2]+=fa[root1];
fa[root1]=root2;
}
return;
}
void Kruskal()
{
memset(fa,-1,sizeof fa);
int cnt=n;//连通块个数
for(int i=0;i<e.size();i++)
{
if(cnt==k)//当连通块个数恰好可以等于k,即这些连通块可用k个卫星进行通信,则退出循环
{
break;
}
int u=e[i].u,v=e[i].v;
double w=e[i].w;
int root1=findRoot(u),root2=findRoot(v);
if(root1!=root2)
{
unionSet(u,v);
cnt--;//每做一次并集操作,连通块个数-1
res=w;//更新答案,当连通块个数恰好为k时,选入的那条件的权值即为当前最大,总体最大最小,即d值
}
}
}
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++)
{
cin>>p[i].x>>p[i].y;
}
for(int i=0;i<n;i++)//连边
{
for(int j=i+1;j<n;j++)
{
e.push_back({i,j,getD(p[i],p[j])});
}
}
sort(e.begin(),e.end());
Kruskal();
printf("%.2f",res);
}