四平方和(二分)

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

每个正整数都可以表示为至多 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值