1221. 四平方和
这个题有很多的方法,
三重循环爆搜(不行)
二分
Hash表(不行)
- C++里的Hash表是unordered_map。里面的查找是S.count(x)S是哈希表,x是要查找的元素
这个题目最朴素的方法是四个for循环,铁定是TLE的。但是可以优化成3重循环:
三重循环(O(N3)
a2 + b2 + c2 + d2 = n
d = sqrt( n - a2 - b2 - c2)
所以只需要三重循环,然后设置一个数d,判断能不能满足即可
//三重循环暴力
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 5e6+10;
int n;
int main()
{
cin>>n;
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 = sqrt(t); //出来可能不是整数,所以下面的if就不满足
if(a*a+b*b+c*c+d*d == n)
{
printf("%d %d %d %d",a,b,c,d);
return 0;
}
}
}
二分 O(N2logn)
现在的空间越来越不值钱,而时间却越来越值钱。所以以后的做题也应该考虑 用空间换时间
因为有4个数,a b c d。所以我们考虑先将a2 + b2用一部分空间存起来,这样的时间复杂度是O(N2)。这里N不是5e6,因为每次最多找到根号n就不找了,所以N = 根号5e6 = 2236 大约为2000,O(N2) = 4*106。
这里存的是一个结构体,包括平方和以及c d。
存起来之后再去循环c 和d ,如果在前面的存的数里找到了 == n - a * a - b * b就可以输出了。
这里的a b c d都是从0开始的,所以找到的第一个一定都是字典序最小的。
//二分 用空间换时间
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 5e6+10;
struct node{
int sum,a,b;
bool operator<(const node &x)const
{
if(sum !=x.sum) return sum<x.sum;
if(a!=x.a) return a<x.a;
return b<x.b;
}
}e[N];
int main()
{
int n;
int k=0;
cin>>n;
for(int c=0;c*c<=n;c++)
for(int d=c;c*c+d*d<=n;d++)
{
e[++k] = {c*c+d*d,c,d};
}
sort(e+1,e+1+k);
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=1,r=k;
while(l<r)
{
int mid = l+r>>1;
if(e[mid].sum >= t) r=mid;
else l=mid + 1 ;
}
if(e[l].sum==t)
{
printf("%d %d %d %d",a,b,e[l].a,e[l].b);
return 0;
}
}
}
Hash表(O(N2))
理论上二分比Hash表慢,但是实际跑的时候二分比Hash快的。因为二分的常数很小,Hash的常熟很大,所谓常熟就可以理解为代码量。
Hash表的key是平方和,值是一个pair,存的是c d.
然后后面直接找一下S.count()有没有,有的话就直接输出。
这里可以直接输出的原因是因为a b c d都是从0开始找的,所以找到的第一个就是字典序最小的
Hash表的查找替换也是。第一次找到的平方和的位置一定是最小的,后面再找到了虽然结果都是平方和,但是字典序大了。
//Hash表方法
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#define x first
#define y second
using namespace std;
const int N = 5e6+10;
typedef pair<int,int> PII;
unordered_map<int,PII> S; //int是平方和,PII存的是a b
int main()
{
int n;
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=a;a*a+b*b<=n;b++)
{
int t = n - a*a-b*b;
if(S.count(t))
{
printf("%d %d %d %d ",a,b,S[t].x,S[t].y);
return 0;
}
}
}