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;
}

原文地址:https://www.cnblogs.com/stelayuri/p/12709971.html