Xenia and Colorful Gems(二分--思维)
给定三个数组a,b,c. 要求从每个数字取一个数,使得两两之差和最小。 求出这个数。 |
\(我又懵逼了。我是会O(n^3)的暴力啊,怎么办。\)
\(\color{Red}{从结果看,选出来的三个数必定存在a<=b<=c}\)
能不能固定一个数或两个数来确定其余数来降低复杂度呢?可以的。
固定b,然后在其他两个数组中找a和c.
\(a一定是刚好小于等于b的,c一定是刚好大于等于b的。\)
那就开始二分吧.......
不过,每个数组都可以作为最小,中间,最大三种可能,所以我们有\(C_3{2}\)种要枚举。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+9;
#define INF 0x7fffffffffffffff
int na,nb,nc,t;
ll a[maxn],b[maxn],c[maxn],ans;
ll ji(ll a,ll b,ll c){
return (a-b)*(a-b)+(a-c)*(a-c)+(b-c)*(b-c);
}
int find_min(ll x,ll a[],int na)
{
int l=1,r=na,mid;
while(r>l)
{
mid=(l+r+1)/2;
if(a[mid]<x) l=mid;
else if(a[mid]>x) r=mid-1;
else
{
l=mid;
break;
}
}
return l;
}
int find_max(ll x,ll a[],int na)
{
int l=1,r=na,mid;
while(r>l)
{
mid=(l+r)/2;
if(a[mid]>x) r=mid;
else if(a[mid]<x) l=mid+1;
else
{
r=mid;
break;
}
}
return r;
}
void solve(ll a[],ll b[],ll c[],int na,int nb,int nc)
{
for(int i=1;i<=nb;i++)//数组b为中间值
{
if(a[1]>b[i]) continue;
if(c[nc]<b[i]) continue;
int q=find_min(b[i],a,na);
int w=find_max(b[i],c,nc);
ans=min(ans,ji(a[q],b[i],c[w]));
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
ans=INF;
scanf("%d%d%d",&na,&nb,&nc);
for(int i=1;i<=na;i++) scanf("%lld",&a[i]);
for(int i=1;i<=nb;i++) scanf("%lld",&b[i]);
for(int i=1;i<=nc;i++) scanf("%lld",&c[i]);
sort(a+1,a+1+na);
sort(b+1,b+1+nb);
sort(c+1,c+1+nc);
solve(a,b,c,na,nb,nc);solve(a,c,b,na,nc,nb);
solve(b,a,c,nb,na,nc);solve(b,c,a,nb,nc,na);
solve(c,a,b,nc,na,nb);solve(c,b,a,nc,nb,na);
cout<<ans<<endl;
}
return 0;
}