zoukankan      html  css  js  c++  java
  • 部落划分 (二分+并查集/kruskal)

    问题:

    聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗。只是,这一切都成为谜团了——聪聪根本就不知道部落究竟是如何分布的。
    不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了N个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了K个部落!这真是个好消息。
    聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:
    对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。 例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。

    解:

    再次明确几点重要的东西 别搞忘了

    1:处理集合问题一定要用并查集

    2: 最近的最远 最远的最近 不是二分就是生成树

    考试的时候想到正解的 突然卡住 难受QWQ

    $1$

    二分一个最小距离

    暴力枚举节点之间的距离,小于枚举的答案就合并成一个集合 

                        两个部落的距离只会比这个更小所以必须合并

    大于的话是另一个集合(考试的时候我就是在这里卡住了 我还以为他会被别的点更新 但实际上是不会的 因为暴力枚举的是所有点的距离)

    并查集套用即可

    //
    #include<bits/stdc++.h>
    using namespace std;
    #define maxnn 100000
    #define ll long long 
    ll n,k;
    ll f[maxnn];
    ll x[maxnn],y[maxnn];
    double dist(ll x1,ll x2,ll y1,ll y2)
    {
        return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }
    int gf(int v)
    {
        if(f[v]==v) return v;
        else return f[v]=gf(f[v]);
    }
    void merge(int v,int u)
    {
        int f1=gf(v);
        int f2=gf(u);
        if(f1!=f2) f[f1]=f2;
    }
    bool isok(double ttt)
    {
        for(int i=1;i<=n;i++)
        {
            f[i]=i;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i!=j)
                {
                    double dis=dist(x[i],x[j],y[i],y[j]);
                    if(dis<ttt) merge(i,j);
                }
            }
            
        }
        int sec=0;
            for(int i=1;i<=n;i++)
        {
            if(f[i]==i) sec++;
        }
        return sec>=k;
    }
    int main()
    {
        
        cin>>n>>k;
        double  l=0,r=12000,eps=1e-1;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld",&x[i],&y[i]);
        }
        int cnt=0;
        while(cnt<=60) 
        {
            cnt++;
            double mid=(l+r)/2;
            if(isok(mid)) l=mid;
            else r=mid;
        }
        printf("%.2lf",l);
    }

    $2$ kruskal 

    注意到最大距离最小  不只可以二分 还可以跑生成树 

    生成树也是处理集合问题的利器

    按照边从小到大排序 

    优先合并小的 直到联通块(集合)的值刚好 ==k;

    输出下一条边

    来自hyhjulao 的code:

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void rin(T &t)
    {t=0;int k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){t=t*10+c-'0';c=getchar();}t*=k;}
    const int maxn=1e3+5;
    struct node
    {
        int a,b;
        double c;
    }e[maxn*maxn];
    int n,k,fa[maxn],tot;
    bool cmp(node x,node y)
    {return x.c<y.c;}
    typedef pair<int,int> P;
    P NODE[maxn];
    inline double dis(int i,int j)
    {return sqrt((NODE[i].first-NODE[j].first)*(NODE[i].first-NODE[j].first)+(NODE[i].second-NODE[j].second)*(NODE[i].second-NODE[j].second));}
    int getfa(int v)
    {return fa[v]==v?v:fa[v]=getfa(fa[v]);}
    int main()
    {
        rin(n);rin(k);
        for(int i=1;i<=n;i++)
        {
            rin(NODE[i].first);rin(NODE[i].second);
            fa[i]=i;
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        {
            e[++tot].a=i;e[tot].b=j;
            e[tot].c=dis(i,j);
        }
        sort(e+1,e+1+tot,cmp);
        int cnt=n,fx,fy;
        double ans;
        for(int i=1;i<=tot;i++)
        {
            fx=getfa(e[i].a);
            fy=getfa(e[i].b);
            if(fx!=fy)
            {
                if(cnt==k){ans=e[i].c;break;}
                else cnt--;
                fa[fx]=fy;
            }
        }
        printf("%.2lf",ans);
    
    }
    刀剑映出了战士的心。而我的心,漆黑且残破
  • 相关阅读:
    C# ListView应用
    C# 使用System.Speech 进行语音播报和识别
    支付宝支付-扫码支付详解
    使用git提交项目到码云
    C# byte[]数组和string的互相转化 (四种方法)
    C#中字节数组byte[]、图片image、流stream,字符串string、内存流MemoryStream、文件file,之间的转换
    C# 结构体和List<T>类型数据转Json数据保存和读取
    对 JSON 数据进行序列化和反序列化
    win10与虚拟机fedora14使用samba文件共享
    serialVersionUID, ObjectInputStream与ObjectOutputStream类,Serializable接口,serialVersionUID的作用和用法
  • 原文地址:https://www.cnblogs.com/OIEREDSION/p/11370731.html
Copyright © 2011-2022 走看看