四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多 4 个正整数的平方和。
如果把 0 包括进去,就正好可以表示为 4 个数的平方和。
比如:
5=02+02+12+22//这里是次方,0的次方+0的次方+1的二次方+2的二次方=5
7=12+12+12+22
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对 4 个数排序:
0≤a≤b≤c≤d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法。
输入格式
输入一个正整数 N。
输出格式
输出4个非负整数,按从小到大排序,中间用空格分开。
数据范围
0<N<5∗106(5*10的6次方)
输入样例:
5
输出样例:
0 0 1 2
分析:
由题目的时间复杂度分析,只能最多枚举其中相加的两个数,因为暴力枚举所有可能会超时,这里采用先枚举两个再枚举两个,这题可以先枚举c和d,把c和d的情况储存进一个结构体,再枚举a和b的情况,剩下的数与c*c+d*d判断是否相等(利用二分查找的思想判断)并输出字典序最小的一组。
而这题不容易理解的地方,就是枚举完两边(a和b,c和d)之后可以保证a>b,c>d,但b和c的大小就很难想明白怎么比较出来,其实当枚举完a和b之后,在满足题意得c和d中,c一定是大于b的,我们可以反过来看,假如c小于b,那是不是a和b就不算字典序最小的符合条件的那一位了,因为可以有a和c组成前面的一组,b和d组成后面的一组,也是符合题意的,但前一组的两个for循环一定是按字典序大小一个一个枚举的,因此后面的c和d就自然肯定大于或等于前面的a和b了。
代码实现:
二分思想(时间复杂度为O(N2logN) )
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=2500010;
int n,m;
//定义一个结构体来储存c*c+d*d,以及对应的c和d,用来后面枚举a,b两种情况的判断
struct Sum
{
int s,c,d;
bool operator<(const Sum&t)const//重载小于号用来排序,把结构体内的三个数按字典序排好
{
if(s!=t.s) return s<t.s;//先把s对比,不同则返回较小的
if(c!=t.c) return c<t.c;//再把c对比,不同则返回较小的
return d<t.d;//否则返回d较小的
}
}sum[N];
int main()
{
cin>>n;
//把c和d的情况枚举出来存入结构体中
for(int c=0;c*c<=n;c++)
{
for(int d=c;c*c+d*d<=n;d++)
{
sum[m++]={c*c+d*d,c,d};
}
}
sort(sum,sum+m);//sort之后让储存的大小具有单调性,用来二分
//枚举a和b的情况,再通过结构体判断
for(int a=0;a*a<=n;a++)
{
for(int b=a;a*a+b*b<=n;b++)
{
int t=n-a*a-b*b;
int l=0,r=m-1;
while(l<r)
{
int mid=l+r>>1;
if(sum[mid].s>=t) r=mid;
else l=mid+1;
}
if(sum[l].s==t)
{
printf("%d %d %d %d",a,b,sum[l].c,sum[l].d);
return 0;
}
}
}
return 0;
}
这里对于用哈希写的方式也放下面了
哈希代码:时间复杂度为O(N2)
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2500010;
int n, m;
unordered_map<int, PII> S;
int main()
{
cin >> n;
for (int c = 0; c * c <= n; c ++ )
for (int d = c; c * c + d * d <= n; d ++ )
{
int t = c * c + d * d;
if (S.count(t) == 0) S[t] = {c, d};
}
for (int a = 0; a * a <= n; a ++ )
for (int b = 0; a * a + b * b <= n; b ++ )
{
int t = n - a * a - b * b;
if (S.count(t))
{
printf("%d %d %d %d\n", a, b, S[t].x, S[t].y);
return 0;
}
}
return 0;
}