四平方和
题目描述
核心思路
由题意可知, a 2 + b 2 + c 2 + d 2 = n a^2+b^2+c^2+d^2=n a2+b2+c2+d2=n,我们可以先用两层循环来求出 c 2 + d 2 c^2+d^2 c2+d2。可以开一个结构体,这个结构体Sum中含有 s s s,表示 c 2 + d 2 c^2+d^2 c2+d2的和,然后还存储 c , d c,d c,d。对这个结构体按从小到大排序,{ c 2 + d 2 , c , d c^2+d^2,c,d c2+d2,c,d},如果 c 2 + d 2 c^2+d^2 c2+d2不相等,则从小到大排序,如果相等,则比较 c c c,如果 c c c不相等,则按从小到大排序,否则再比较 d d d,如果 d d d相等,则按从小到大排序,否则说明这两组数据是完全相等的。之所以这么做,主要是题目说 a , b , c , d a,b,c,d a,b,c,d升序排列。因此,我们对c和d枚举时,要保证 c < d c<d c<d。
然后我们用两层循环来枚举a,b。对于每次枚举,求出 t = n − a 2 − b 2 t=n-a^2-b^2 t=n−a2−b2,然后我们去sum结构体数组中找到有没有某个s是等于t的。那么如何快速地判断某个数是否出现在一堆数中呢?也就是在数组中有没有与target相等的数。这个可以用二分算法和哈希表。
常见的快速地判断某个数是否出现在一堆数中的写法:
#include<iostream>
using namespace std;
//判断某个数target在一堆数中是否出现过
//即看看数组中是否有与target相等的元素
int Binary_Search(int a[],int target)
{
int l=0,r=4;
while(l<r)
{
//第一种写法
int mid=l+r>>1;
if(a[mid]>=target)
r=mid;
else
l=mid+1;
//第二种写法
// int mid=l+r+1>>1;
// if(a[mid]<=target)
// l=mid;
// else
// r=mid-1;
}
if(a[r]==target)
return r;
else
return -1;
}
int main()
{
int a[5]={1,2,3,4,5};
cout <<Binary_Search(a,3)<<endl;
return 0;
}
这里用二分算法来求解。比如设t=4,由于结构体从小到大排序了,比如此时sum数组中的s为{1,2,4,4,4,8}。由于题目要求输出第一个,也就是说此时是找左侧边界,即此时需要输入数组下标为2的那个4。因此,我们用的是 s u m [ m i d ] . s > = t sum[mid].s>=t sum[mid].s>=t而不是 s u m [ m i d ] . s < = t sum[mid].s<=t sum[mid].s<=t,因为第一种写法是可以找到大于等于某个数的最小数,即最大值的最小,即左侧边界。而第二种写法可以找到小于等于某个数的最大数,即最小值的最大,即右侧边界。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e6+10;
struct Sum{
int s,c,d;
bool operator < (const Sum &W)const
{
if(s!=W.s)
return s<W.s;
if(c!=W.c)
return c<W.c;
if(d!=W.d)
return d<W.d;
}
}sum[N];
int n,m;
int main()
{
scanf("%d",&n);
//先打表处理各组c*c+d*d
for(int c=0;c*c<=n;c++)
for(int d=c;d*d<=n;d++)
sum[m++]={c*c+d*d,c,d};
sort(sum,sum+m);//按从小到大排序
for(int a=0;a*a<=n;a++)//枚举a
{
for(int b=a;b*b<=n;b++)//b从a开始,保证b>a
{
int t=n-a*a-b*b;
int l=0,r=m-1;
//二分找到大于等于t的最小的那个
//这样保证找到的是题目要求输出的 "第一个"
while(l<r)
{
int mid=l+r>>1;
if(sum[mid].s>=t)
r=mid;
else
l=mid+1;
}
//有可能sum数组中并不存在于t相等的数 因此退出while循环后要判断一下
if(sum[r].s==t)
{
printf("%d %d %d %d\n",a,b,sum[r].c,sum[r].d);
return 0;//二分找到的第一个r肯定是题目要求的 "第一个"
}
}
}
return 0;
}