蓝桥杯第七届省赛B组———四平方和

四平方和定理,又称为拉格朗日定理:

每个正整数都可以表示为至多 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
输入样例:
5
输出样例:
0 0 1 2

解法1:

用哈希表map,找出所有 c 方加 d 方的值所对应的 c 的值,遍历a 和 b 求出 c 方加 d 方,找到对应的 c 的值,然后利用n - a方 - b方 - c方求出 d方,对d方开根号

#include <iostream>
#include <unordered_map>
#include <cmath>
#include <cstdio>
using namespace std;
const int max = 10000000;
unordered_map <int, int> f;
int main()
{
	int n;
	scanf("%d", &n);
	for (int c = 0; c * c * 2 <= n; c++) {
		for (int d = c; d * d + c * c <= n; d++) {
			if (f.find(d * d + c * c) == f.end()) {
				f[d *d + c * c] = c;
			}
		}
	}
	for (int a = 0; a * a * 4 <= n; a++) {
		for (int b = a; b * b * 3 <= n; b++) {
			if (f.find(n - a * a - b * b) != f.end()) {//找不到n - a * a - b * b说明a,b不满足情况
				int c = f[n - a * a - b * b];
				int d = (int)sqrt(n - a * a - b * b - c * c);
				//cout << a << " " << b << " " << c << " " << d << endl;
				printf("%d %d %d %d", a, b, c ,d);
				return 0;
			}
		}
	}
	return 0;
}

改进:因为map中的find的操作比较费时,所以有些数据还会过不了,故改用大数组模拟map

#include <iostream>
#include <unordered_map>
#include <cmath>
#include <cstdio>
using namespace std;
const int max1 = 10000000;
int f[max1];
int main()
{
	int n;
	scanf("%d", &n);
	for (int c = 0; c * c * 2 <= n; c++) {
		for (int d = c; d * d + c * c <= n; d++) {
			if (!f[d *d + c * c]) {
				f[d *d + c * c] = c + 1;//存在c = 0的情况,在下面的if判断中会跳过,所以加一
			}
		}
	}
	for (int a = 0; a * a * 4 <= n; a++) {
		for (int b = a; b * b * 3 <= n; b++) {
			if (f[n - a * a - b * b]) {
				int c = f[n - a * a - b * b] - 1;
				int d = (int)sqrt(n - a * a - b * b - c * c);
				printf("%d %d %d %d", a, b, c ,d);
				return 0;
			}
		}
	}
	return 0;
}

解法2:

相当于把map中的find,改成二分查找来节省时间
这个是看的别人的文章,写的很不错

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 2500010;

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;
        return d < t.d;
    }
}sum[N];

int n, m;

int main()
{
    cin >> n;

    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};//用一个结构体存储后两位,满足<=n的所有方案

    sort(sum, sum + m);//从小到大排序

    for (int a = 0; a * a <= n; a ++ )
        for (int b = a; a * a + b * b <= n; b ++ )
        {   //重新找满足前两位 <= n 的方案
            int t = n - a * a - b * b;
            int l = 0, r = m - 1;
            //利用二分查找,在sum中找到t, 如果找到,则输出,因为我们的a,b都是从小到大找的,所以,找到的第一个方案就是满足题目要求的
            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\n", a, b, sum[l].c, sum[l].d);
                return 0;
            }
        }

    return 0;
}

作者:ao_wu
链接:https://www.acwing.com/solution/content/22419/
来源:AcWing
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值