Codeforces 1336B/1337D - Xenia and Colorful Gems (二分/思維)



題面

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<<'\n';
}
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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM