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

猜你喜欢

转载自www.cnblogs.com/iss-ue/p/12792161.html