题目链接:[蓝桥杯 2016 省 AB] 四平方和 - 洛谷
思路:
将ab、cd分为两组,先遍历a,b,记录一个三元组(a,b,a*a+b*b);
再遍历c,d,设t=n-a*a-b*b,那么现在就要找一个a*a+b*b=t的三元组
注意点:如果a*a+b*b的三元组已经被创建,那么就无需再创建了(因为要保证字典序最小)
1.用pair数组记录ab平方和
#include<iostream>
using namespace std;
const int N = 5000005;
typedef pair<int,int>PII;
PII f[N];
bool flag[N];
int n;
bool have_ans;
int main()
{
cin>>n;
for(int a=0;a*a<=n;a++)
{
for(int b=a;a*a+b*b<=n;b++)
{
if(flag[a*a+b*b]==false) f[a*a+b*b]=make_pair(a,b);
flag[a*a+b*b]=true;
}
}
for(int c=0;c<n;c++)
{
for(int d=c;c*c+d*d<=n;++d)
{
int t=n-c*c-d*d;
if(flag[t])
{
have_ans=true;
cout<<c<<" "<<d<<" "<<f[t].first<<" "<<f[t].second;
break;
}
}
if(have_ans) break;
}
return 0;
}
2.二分查找
#include<iostream>
#include<algorithm>
using namespace std;
struct Num
{
int c,d,sum;
bool operator<(const Num a)
{
if(sum!=a.sum) return sum<a.sum;
if(c!=a.c) return c<a.c;
return d<a.d;
}
};
Num num[5000005];
int cnt=0;
int main()
{
int n;
bool flag=false;
cin>>n;
for(int i=0;i*i<=n;++i)
{
for(int j=i;i*i+j*j<=n;++j)
{
num[cnt++]=Num{i,j,i*i+j*j};
}
}
sort(num,num+cnt);
for(int i=0;i*i<=n;++i)
{
for(int j=i;j*j+i*i<=n;++j)
{
int t=n-i*i-j*j;
int l=0,r=cnt-1,mid;
while(l<r)
{
mid=(l+r)>>1;
if(t>num[mid].sum) l=mid+1;
else r=mid;
}
if(t==num[l].sum)
{
cout<<i<<" "<<j<<" "<<num[l].c<<" "<<num[l].d<<endl;
flag=true;
break;
}
}
if(flag) break;
}
return 0;
}
3.用f数组和flag数组记录ab平方和
f【i】记录平方和为i的ab两数
flag【i】记录平方和为i的 f数组下标,通过此快速访问到ab
#include<iostream>
using namespace std;
const int N = 5000005;
int flag[N];
int n;
bool have_ans;
struct Num
{
int a,b;
int sum;
bool operator<(const Num x)const{
return sum<x.sum;
}
}num[N];
int cnt;
int main()
{
cin>>n;
for(int a=0;a*a<=n;a++)
{
for(int b=a;a*a+b*b<=n;b++)
{
if(!flag[a*a+b*b])
{
num[++cnt]=Num{a,b,a*a+b*b};
flag[a*a+b*b]=cnt;
}
}
}
for(int c=0;c<n;c++)
{
for(int d=c;c*c+d*d<=n;++d)
{
int t=n-c*c-d*d;
if(flag[t])
{
have_ans=true;
cout<<c<<" "<<d<<" "<<num[flag[t]].a<<" "<<num[flag[t]].b;
break;
}
}
if(have_ans) break;
}
return 0;
}