122. 四平方和(暴力枚举)
2016年蓝桥杯省赛 - 四平方和(困难)
标签:2016 省赛 暴力枚举
题目描述
四平方和定理,又称拉格朗日定理:每个正整数都可以表示为至多 4 个正整数的平方和。把 0 也算进去,就正好可以表示为 4 个数的平方和,比如
5=02+02+12+22 5 = 0^2 + 0^2 + 1^2 + 2^2 5=02+02+12+22
7=12+12+12+22 7 = 1^2 + 1^2 + 1^2 + 2^2 7=12+12+12+22
一个正整数可能存在多种表示法。要求把 4 个数排序为 0≤a≤b≤c≤d0 \le a \le b \le c \le d0≤a≤b≤c≤d,并把所有表示法按 a,b,c,da, b, c, da,b,c,d 为联合主键升序排列,输出排在最前面的那一组。
输入描述
一个正整数 NNN(N<5000000N < 5000000N<5000000)。
输出描述
4 个非负整数,按从小到大排序,中间用空格分开。
输入输出样例
示例 1:
输入
txt
5
输出
txt
0 0 1 2
示例 2:
输入
txt
12
输出
txt
0 2 2 2
示例 3:
输入
txt
773535
输出
txt
1 1 267 838
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
解题思路
四个数要满足 a2+b2+c2+d2=Na^2 + b^2 + c^2 + d^2 = Na2+b2+c2+d2=N,并且 0≤a≤b≤c≤d0 \le a \le b \le c \le d0≤a≤b≤c≤d,输出字典序最小的那一组。
直接四重循环枚举四个数太慢,但注意到:只要 a,b,ca, b, ca,b,c 定了,ddd 也就定了------剩下的部分必须是一个完全平方数。所以先算出
d=⌊N−a2−b2−c2⌋ d = \left\lfloor \sqrt{N - a^2 - b^2 - c^2} \right\rfloor d=⌊N−a2−b2−c2 ⌋
再回头验证 a2+b2+c2+d2=Na^2 + b^2 + c^2 + d^2 = Na2+b2+c2+d2=N 就行。这样四重循环直接降成三重。
把 NNN 看成一条长条就是:a2a^2a2、b2b^2b2、c2c^2c2 之外剩下的那截,必须正好等于 d2d^2d2,否则这组 (a,b,c)(a,b,c)(a,b,c) 作废:

枚举时每一层都从上一个数开始(b≥ab \ge ab≥a、c≥bc \ge bc≥b、d≥cd \ge cd≥c),顺序既不重复也不遗漏;同时用 ⋅\sqrt{\cdot}⋅ 收紧每层的上界,比如给定 aaa 后 bbb 最多取到 N−a2\sqrt{N - a^2}N−a2 ,可以省掉大量不可能的取值。
又因为 a,b,c,da, b, c, da,b,c,d 是从小到大一层层枚举的,第一组满足等式的解,恰好就是按 a,b,c,da, b, c, da,b,c,d 升序排在最前面的那组,找到直接返回即可。
以样例 773535 走一遍:
txt
N = 773535
a=0 整个分支试遍,无解
a=1, b=1
c=1 ... 266 剩余部分都不是完全平方数
c=267 剩余 = 773535 - 1 - 1 - 267² = 702244
d = √702244 = 838,且 838 ≥ 267
→ 输出 1 1 267 838
代码实现
python
import math
def hs(n):
am = int(math.sqrt(n)) # a 最大值
for a in range(am + 1):
bm = int(math.sqrt(n - a * a)) # 给定a后,b的最大值
for b in range(a, bm + 1): # 保证 a <= b
cm = int(math.sqrt(n - a * a - b * b)) # 给定a和b后,c的最大值
for c in range(b, cm + 1): # 保证 b <= c
t = n - a * a - b * b - c * c # 剩下的部分应等于 d^2
d = int(math.sqrt(t)) # 计算d
if d >= c and a * a + b * b + c * c + d * d == n:
return a, b, c, d
n = int(input())
print(' '.join([str(x) for x in hs(n)]))
复杂度分析
- 时间复杂度 :O(N3/2)O(N^{3/2})O(N3/2),三层枚举 a,b,ca, b, ca,b,c,每层的上界都是 N\sqrt{N}N 级别;找到第一组解就返回,实际往往远小于这个上界。
- 空间复杂度 :O(1)O(1)O(1),只用了几个循环变量和临时变量。