刷题记录-luoguP1991 无线通讯网

来源:互联网 发布:ubuntu卸载samba 编辑:程序博客网 时间:2024/05/20 17:40

这题是典型的二分答案

一开始用最小生成树做,用S(卫星电话)把最大边删去,发现并不是最优解。

二分枚举D,小于等于D的边才可以“发挥作用”,用并查集计算连通分量的个数,

只有连通分量的个数小于等于S时,才认为可行。

*******************************************

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define pii pair<int,int>
#define MAXV 505
#define MAXE 250005
using namespace std;
struct edge{
    int u,v;
    double cost;
};
edge e[MAXE];
int S,P,E;
double D;
pii p[MAXV];
int f[MAXV];
int find(int x){
    return (f[x]==x)?x:(f[x]=find(f[x]));
}
void init_find(){
    for(int i=1;i<=P;i++){
        f[i]=i;
    }
}
void lik(int x,int y){
    x=find(x),y=find(y);
    if(x!=y){
        f[x]=y;
    }
}
bool comp(const edge &e1,const edge &e2){
    return (e1.cost<e2.cost);
}
int check(){
    init_find();
    for(int i=1;i<=E;i++){
        if(e[i].cost<=D){
            lik(e[i].u,e[i].v);
        }
    }
    int cnt=0;
    int b[MAXV]={0};
    for(int i=1;i<=P;i++){
        int x=find(i);
        if(!b[x]){
            cnt++;
            b[x]=1;
        }
    }
    if(cnt>S){
        return 0;
    }
    else{
        return 1;
    }
}
int main()
{
//    freopen("data.in","r",stdin);
    scanf("%d%d",&S,&P);
    for(int i=1;i<=P;i++){
        scanf("%d%d",&p[i].first,&p[i].second);
    }
    for(int i=1;i<=P;i++){
        for(int j=i+1;j<=P;j++){
            e[++E].u=i; e[E].v=j;
            e[E].cost=sqrt((double)(p[i].first-p[j].first)*(p[i].first-p[j].first)+(double)(p[i].second-p[j].second)*(p[i].second-p[j].second));
        }
    }
    sort(e+1,e+E+1,comp);
    int l=1,r=E;
    while(l<r){
        int mid=l+(r-l)/2;
        D=e[mid].cost;
        if(check()){
            r=mid;
        }
        else{
            l=mid+1;
        }
    }
    printf("%.2f\n",e[l].cost);
    return 0;
}

原创粉丝点击