四平方和

四平方和

题目描述

在这里插入图片描述


核心思路

由题意可知, a 2 + b 2 + c 2 + d 2 = n a^2+b^2+c^2+d^2=n a2+b2+c2+d2=n,我们可以先用两层循环来求出 c 2 + d 2 c^2+d^2 c2+d2。可以开一个结构体,这个结构体Sum中含有 s s s,表示 c 2 + d 2 c^2+d^2 c2+d2的和,然后还存储 c , d c,d c,d。对这个结构体按从小到大排序,{ c 2 + d 2 , c , d c^2+d^2,c,d c2+d2,c,d},如果 c 2 + d 2 c^2+d^2 c2+d2不相等,则从小到大排序,如果相等,则比较 c c c,如果 c c c不相等,则按从小到大排序,否则再比较 d d d,如果 d d d相等,则按从小到大排序,否则说明这两组数据是完全相等的。之所以这么做,主要是题目说 a , b , c , d a,b,c,d a,b,c,d升序排列。因此,我们对c和d枚举时,要保证 c < d c<d c<d

然后我们用两层循环来枚举a,b。对于每次枚举,求出 t = n − a 2 − b 2 t=n-a^2-b^2 t=na2b2,然后我们去sum结构体数组中找到有没有某个s是等于t的。那么如何快速地判断某个数是否出现在一堆数中呢?也就是在数组中有没有与target相等的数。这个可以用二分算法和哈希表。

常见的快速地判断某个数是否出现在一堆数中的写法:

#include<iostream>
using namespace std;
//判断某个数target在一堆数中是否出现过
//即看看数组中是否有与target相等的元素
int Binary_Search(int a[],int target)
{
    int l=0,r=4;
    while(l<r)
    {
        //第一种写法
        int mid=l+r>>1;
        if(a[mid]>=target)
            r=mid;
        else
            l=mid+1;

        //第二种写法
        // int mid=l+r+1>>1;
        // if(a[mid]<=target)
        //     l=mid;
        // else
        //     r=mid-1;
    }
    if(a[r]==target)
        return r;
    else
        return -1;
}
int main()
{
    int a[5]={1,2,3,4,5};
    cout <<Binary_Search(a,3)<<endl;
    return 0;
}

这里用二分算法来求解。比如设t=4,由于结构体从小到大排序了,比如此时sum数组中的s为{1,2,4,4,4,8}。由于题目要求输出第一个,也就是说此时是找左侧边界,即此时需要输入数组下标为2的那个4。因此,我们用的是 s u m [ m i d ] . s > = t sum[mid].s>=t sum[mid].s>=t而不是 s u m [ m i d ] . s < = t sum[mid].s<=t sum[mid].s<=t,因为第一种写法是可以找到大于等于某个数的最小数,即最大值的最小,即左侧边界。而第二种写法可以找到小于等于某个数的最大数,即最小值的最大,即右侧边界


代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e6+10;
struct Sum{
    int s,c,d;
    bool operator < (const Sum &W)const
    {
        if(s!=W.s)
            return s<W.s;
        if(c!=W.c)
            return c<W.c;
        if(d!=W.d)
            return d<W.d;
    }
}sum[N];
int n,m;
int main()
{
    scanf("%d",&n);
    //先打表处理各组c*c+d*d
    for(int c=0;c*c<=n;c++)
        for(int d=c;d*d<=n;d++)
            sum[m++]={c*c+d*d,c,d};
    sort(sum,sum+m);//按从小到大排序
    for(int a=0;a*a<=n;a++)//枚举a
    {
        for(int b=a;b*b<=n;b++)//b从a开始,保证b>a
        {
            int t=n-a*a-b*b;
            int l=0,r=m-1;
            //二分找到大于等于t的最小的那个
            //这样保证找到的是题目要求输出的 "第一个"
            while(l<r)
            {
                int mid=l+r>>1;
                if(sum[mid].s>=t)
                    r=mid;
                else
                    l=mid+1;
            }
            //有可能sum数组中并不存在于t相等的数  因此退出while循环后要判断一下
            if(sum[r].s==t)
            {
                printf("%d %d %d %d\n",a,b,sum[r].c,sum[r].d);
                return 0;//二分找到的第一个r肯定是题目要求的 "第一个"
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计小酱蟹不肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值