題面
題意
給定三個數組
要求從每個數組中拿出一個數
問這三個數兩兩之差的平方和的最小值
解題思路
雖然說是思路,實際上有些瞎搞吧
下面可能會看上去有點亂
對於這種題目,我只知道的結論只有一個:
如果已經固定了兩個數 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;
}