※P4047 [JSOI2010]部落划分

一眼二分:
二分出最近的两个部落之间的最大距离,判断当前mid距离下能否划分成k个连通块

const int N=1010;
PII a[N];
int p[N];
double dist[N][N];
int n,k;

double dis(PII a,PII b)
{
    return sqrt((a.fi-b.fi)*(a.fi-b.fi)+(a.se-b.se)*(a.se-b.se));
}

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

bool check(double mid)
{
    for(int i=0;i<n;i++) p[i]=i;

    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
        {
            if(dist[i][j] > mid) continue;
            int pi=find(i),pj=find(j);
            p[pi]=pj;
        }

    int cnt=0;
    for(int i=0;i<n;i++)
        if(p[i] == i)
            cnt++;
    return cnt>=k;
}

int main()
{
    cin>>n>>k;

    for(int i=0;i<n;i++) cin>>a[i].fi>>a[i].se;

    double l=INF,r=0;
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
        {
            dist[i][j]=dis(a[i],a[j]);
            l=min(l,dist[i][j]);
            r=max(r,dist[i][j]);
        }

    for(int i=0;i<100;i++)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }

    printf("%.2f\n",l);

    //system("pause");
}

贪心解法:

  • 要使得两个连通块之间的最短距离尽量大,则将距离短的点集构成一块连通块
  • \(kruskal\)每加一条边连通块数减一,于是采用\(kruskal\)算法求得k个的连通块
  • 则这k个连通块之间的边权大于任一连通块内的边权
  • 答案为使得k个连通块减少为k-1个连通块的边的权值
const int N=1010;
struct Node
{
    int a,b;
    double c;
    bool operator<(const Node &W) const
    {
        return c<W.c;
    }
}e[N*N];
PII a[N];
int p[N];
int n,m,k;

double dis(PII a,PII b)
{
    return sqrt((a.fi-b.fi)*(a.fi-b.fi)+(a.se-b.se)*(a.se-b.se));
}

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

double kruskal()
{
    for(int i=0;i<n;i++) p[i]=i;

    int sum=n;
    for(int i=0;i<m;i++)
    {
        int a=e[i].a,b=e[i].b;
        double c=e[i].c;
        int pa=find(a),pb=find(b);
        if(pa != pb)
        {
            p[pa]=pb;
            sum--;
            if(sum == k-1) return c;
        }
    }
}

int main()
{
    cin>>n>>k;

    for(int i=0;i<n;i++) cin>>a[i].fi>>a[i].se;

    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
            e[m++]={i,j,dis(a[i],a[j])};

    sort(e,e+m);

    double t=kruskal();
    printf("%.2f\n",t);

    //system("pause");
}
posted @ 2020-09-08 11:57  Dazzling!  阅读(136)  评论(0编辑  收藏  举报