👨🏫 参考链接
🙈 题目
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多 4个正整数的平方和。
如果把 0包括进去,就正好可以表示为 4个数的平方和。
比如:
5=02+02+12+22
7=12+12+12+22
对于一个给定的正整数,可能存在多种平方和的表示法。要求你对 4
个数排序:
0≤a≤b≤c≤d
并对所有的可能表示法按 a,b,c,d
为联合主键升序排列,最后输出第一个表示法。
🙈 输入格式
输入一个正整数 N
🙈 输出格式
输出4个非负整数,按从小到大排序,中间用空格分开。
🙈 数据范围
0<N<5∗106
🙈 输入1
5
🙈 输出1
0 0 1 2
🤠 数据范围 + 时间限制 = 可以枚举多少个数
🤠 结果 a b c d ,枚举的时候可人为规定 a < b < c < d
🤠 只能枚举两个数 c,d 存下所有 c^2 + d^2 的 和
🤠 后边再枚举 a,b 算下 n - ( a^2 + b^2)是否有在前边的 a b 平方和出现过,出现过即是一个解
🤠 如何求字典序最小呢?
c d 是从小到大枚举的,而且 c <= d ,
a b 也是从小到大枚举的,第一个解就是最小字典序,然后 n - (a^2 + b^2 ) = t ,在 c d 中找到第一个 平方和等于 t 的就是最小字典序,所以说,枚举 c d 的时候相同的 平方和 t 只用存储最小字典序的那对也就是 第一次出现的
🧐 有意思的是,此题暴力枚举三个数的效率反而是最高的
🧐 理论时间复杂度不一定准确,一切以实际为准,受教了
👵 暴力解法 O( n^3 )
import java.io.*;
import java.util.*;
public class 四平方和
{
public static void main(String[] args)
{
int n = new Scanner(System.in).nextInt();
for (int a = 0; a * a <= n; a++)
for (int b = a; a * a + b * b <= n; b++)
for (int c = b; a * a + b * b + c * c <= n; c++)
{
int t = n - (a * a + b * b + c * c);
int d = (int) Math.sqrt(t);
if (d * d == t)
{
System.out.println(a + " " + b + " " + c + " " + d);
System.exit(0);
}
}
}
}
👵 模拟哈希 O( n^2 )
import java.util.Arrays;
import java.util.Scanner;
// 模拟哈希
public class 四平方和2
{
static int N = 5000010;
static int[] C = new int[N];// 存 c,顺便用 -1 代表 null,间接实现去重,记录 平方和是 index 的最新字典序 c d
static int[] D = new int[N];// 存 d
public static void main(String[] args)
{
int n = new Scanner(System.in).nextInt();
Arrays.fill(C, 0, n + 1, -1);
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 (C[t] == -1)
{
C[t] = c;
D[t] = d;
}
}
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);
if (C[t] != -1)
{
System.out.println(a + " " + b + " " + C[t] + " " + D[t]);
System.exit(0);
}
}
}
}
👵 二分 O( n^2 logn) (此题不建议,得自定义字典序排序函数)
👵 大佬代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5000010;
int n, m;
struct Sum//维护字典序
{
int s, c, d;
bool operator< (const Sum &t) const//重载小于号
{
if (s != t.s) return s < t.s;//先按总和从小到大排序
if (c != t.c) return c < t.c;//若总和相同,则按照c从小到大排序
return d < t.d; //若总和与c均相同,则按照d从小到大排序
}
}S[N];
int main()
{
scanf("%d", &n);
for (int c = 0; c * c <= n; c ++)
for (int d = c; c * c + d * d <= n; d ++)
S[m ++] = {c * c + d * d, c, d};
sort(S, S + m);
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 (S[mid].s >= t) r = mid;
else l = mid + 1;
}
if (S[l].s == t)
{
printf("%d %d %d %d\n", a, b, S[l].c, S[l].d);
return 0;
}
}
return 0;
}