• Codeforces 1336B/1337D



    题面

    Prob




    题意

    给定三个数组

    要求从每个数组中拿出一个数

    问这三个数两两之差的平方和的最小值




    解题思路

    虽然说是思路,实际上有些瞎搞吧

    下面可能会看上去有点乱


    对于这种题目,我只知道的结论只有一个:

    如果已经固定了两个数 a b ,且保证第三个数 c 在这两个数之间

    那么当 c 位于 a b 的正中间时能取到最小值,如果 c 靠近 a 或者 b 的任意一侧,答案值会变大(类似二次函数开口向上图像)


    所以,我的想法是,在第一个数组 R 里枚举第一个数,下标为 i (所有下标从0开始,数组已从小到大排序)

    在第二个数组 G 里二分查找第一个大于等于 R[i] 的数,下标为 gtmp

    那么可能的答案下标就只有 gtmp (大于等于R[i]) 和 gtmp-1(小于R[i])(如果它们存在的话)

    两种情况都考虑一遍,这里以 gtmp 为例

    现在已经固定了 R[i] 和 G[gtmp] 两个数

    根据上面的结论,我们应该在第三个数组里寻找最靠近 (R[i]+G[gtmp])/2 的数

    同样二分查找,获得第一个大于等于这个数的下标,定为 btmp

    所以在第三个数组里可能的下标就是 btmp 和 btmp-1

    下面再以 btmp 为例

    此时可以先拿 R[i] / G[gtmp] / B[btmp] 这三个计算一次答案并取小

    但也有可能发生这种情况——

    我们上面二分查找 btmp 时,以 (R[i]+G[gtmp])/2 作为索引查找,所以这里假设了 R[i] 和 G[gtmp] 就包含了这三个数的最小值与最大值,而 B[btmp] 应该在这两个值以内

    实际上也有可能出现 B[btmp] 在 R[i] ~ G[gtmp] 以外的情况

    (这种情况在第4 5这两个样例中都有出现,以第4个样例为例子,枚举到第一个数字是 2 ,最靠近的位于第二个数组中的数字是 3 ,以 (2+3)/2=2 为索引二分出来的最接近的第三个数字是 6 ,很容易发现 6 已经超出了 2~3 这个范围,且如果以 6 为最大值,实际上第二个数字为 4 时才能取到答案的最小值)

    所以,我们保持第一个数组 R 的下标 i 固定不变,以此时的 gtmp 作为最值,再次在第二个数组中以 (R[i]+B[btmp])/2 为索引二分,获得新的下标 gtmp2

    同样,以 gtmp2 或者 gtmp2-1 作为第二个数组的下标,再计算取一次答案才能考虑完全

    以上就是瞎搞想法,详见代码




    完整程序

    实际上大部分都是复制粘贴

    (Pretests: 186ms/3000ms)

    (System Tests: 233ms/3000ms)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll LINF=0x3f3f3f3f3f3f3f3f;
    
    ll R[100050],G[100050],B[100050];
    
    inline ll cal(ll a,ll b,ll c)
    {
        return (a-b)*(a-b)+(a-c)*(a-c)+(b-c)*(b-c);
    }
    
    void solve()
    {
        int nr,ng,nb;
        cin>>nr>>ng>>nb;
    
        for(int i=0;i<nr;i++)
            cin>>R[i];
        for(int i=0;i<ng;i++)
            cin>>G[i];
        for(int i=0;i<nb;i++)
            cin>>B[i];
        sort(R,R+nr);
        sort(G,G+ng);
        sort(B,B+nb);
    
        ll ans=LINF;
        int gtmp,btmp,gtmp2;
        for(int i=0;i<nr;i++) //枚举第一个数组中的下标 i
        {
            gtmp=lower_bound(G,G+ng,R[i])-G; //以R[i]为索引二分查找第二个数组中第一个大于等于的数字下标
            if(gtmp<ng) //gtmp下标如果合法
            {
                btmp=lower_bound(B,B+nb,(R[i]+G[gtmp])>>1)-B; //以(R[i]+G[gtmp])/2为索引二分第三个数字下标btmp
                if(btmp<nb) //btmp下标如果合法
                {
                    ans=min(ans,cal(R[i],G[gtmp],B[btmp])); //计算一次此时的答案
                    gtmp2=lower_bound(G,G+ng,(R[i]+B[btmp])>>1)-G; //固定i和btmp,以(R[i]+B[btmp])/2为索引重新二分寻找第二个数字下标gtmp2
                    if(gtmp2<ng)
                        ans=min(ans,cal(R[i],G[gtmp2],B[btmp])); //合法时计算答案,下同
                    if(gtmp2>0)
                        ans=min(ans,cal(R[i],G[gtmp2-1],B[btmp]));
                }
                if(btmp>0) //btmp-1下标如果合法
                {
                    ans=min(ans,cal(R[i],G[gtmp],B[btmp-1]));
                    gtmp2=lower_bound(G,G+ng,(R[i]+B[btmp-1])>>1)-G;
                    if(gtmp2<ng)
                        ans=min(ans,cal(R[i],G[gtmp2],B[btmp-1]));
                    if(gtmp2>0)
                        ans=min(ans,cal(R[i],G[gtmp2-1],B[btmp-1]));
                }
            }
            if(gtmp>0)//gtmp-1下标如果合法
            {
                btmp=lower_bound(B,B+nb,(R[i]+G[gtmp-1])>>1)-B;
                if(btmp<nb)
                {
                    ans=min(ans,cal(R[i],G[gtmp-1],B[btmp]));
                    gtmp2=lower_bound(G,G+ng,(R[i]+B[btmp])>>1)-G;
                    if(gtmp2<ng)
                        ans=min(ans,cal(R[i],G[gtmp2],B[btmp]));
                    if(gtmp2>0)
                        ans=min(ans,cal(R[i],G[gtmp2-1],B[btmp]));
                }
                if(btmp>0)
                {
                    ans=min(ans,cal(R[i],G[gtmp-1],B[btmp-1]));
                    gtmp2=lower_bound(G,G+ng,(R[i]+B[btmp-1])>>1)-G;
                    if(gtmp2<ng)
                        ans=min(ans,cal(R[i],G[gtmp2],B[btmp-1]));
                    if(gtmp2>0)
                        ans=min(ans,cal(R[i],G[gtmp2-1],B[btmp-1]));
                }
            }
        }
        cout<<ans<<'
    ';
    }
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        for(int t=1;t<=T;t++)
            solve();
        return 0;
    }
    

  • 相关阅读:
    FloatingActionButton
    OpenWrt for WR720N
    OpenWrt
    shell修改文件名(二)
    shell修改文件名(一)
    POJ 1300 Door Man(欧拉通路)
    USACO zerosum DFS 1A
    hadoop源代码解读namenode高可靠:HA;web方式查看namenode下信息;dfs/data决定datanode存储位置
    Leetcode-subsets
    单链表的各种操作 笔试 面试
  • 原文地址:https://www.cnblogs.com/stelayuri/p/12709971.html
Copyright © 2020-2023  润新知