Project Euler 36-40题

来源:互联网 发布:哪个听书软件最好 编辑:程序博客网 时间:2024/06/07 11:52

第36题

这里写图片描述
题目来源ProjectEuler

这一题求的是小于1,000,000的所有数中,在十进制和二进制表示下都是回文数的数的和。
枚举每个数,检验即可,复杂度O(nlogn)
另一种方法,可枚举前一半的位上的数字,然后将整个数加上去,由于这样产生的所有数都是结果的一部分,省去了许多冗余的运算,会更快一些。但是代码复杂度更高一些。
我使用的是第一种方法:

#include<bits/stdc++.h>using namespace std;bool check(int x){    int a[25]={0};    int tmp=x,cnt=0;    while(tmp){        a[++cnt]=tmp%10;        tmp/=10;    }    for (int i=1;i<=cnt;i++){        if (a[i]!=a[cnt-i+1])   return false;    }    tmp=x;cnt=0;    while(tmp){        a[++cnt]=tmp&1;        tmp>>=1;    }    for (int i=1;i<=cnt;i++){        if (a[i]!=a[cnt-i+1])   return false;    }    return true;}int main(){    long long ans=0;    for (int i=1;i<1000000;i++){        if (check(i))   ans+=i;    }    cout<<ans<<endl;    return 0;}



第37题

这里写图片描述
题目来源ProjectEuler

这一题是有所有这样素数的和:1.它不是一位数、2.它的所有前缀和后缀都是素数
题目中保证了结果只有11个素数的和,但没有指明最大值。只能尝试着利用线性筛求出1000w以内的所有素数,从小到大依次检查,直到检查出11个为止。

#include<bits/stdc++.h>using namespace std;const int maxn=10000000;int prime[maxn+1],cnt;int num[maxn+1];bool check(int x){    for (int i=10;i/10<x;i*=10){        if (num[x%i])   return false;    }    while(x){        if (num[x]) return false;        x/=10;    }    return true;}int main(){    cnt=0;    num[1]=1;    for(int i=2;i<=maxn;i++){        if(num[i]==0)   prime[++cnt]=i;        for (int j=1;j<=cnt&&i*prime[j]<=maxn;j++){            num[i*prime[j]]=1;        }    }    int n=0,ans=0;    for (int i=5;n<11;i++){        if (check(prime[i])){            n++;ans+=prime[i];        }    }    cout<<ans<<endl;    return 0;}



第38题

这里写图片描述
题目来源ProjectEuler

对于1-9的全排列构成的所有数,有一部分数可以表示成(x1)(x2)(x3)(xn)¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯,求最大的这样的数。题中给出了当x=192时的例子。
由于要求n>1,所以x<10000,枚举x,当(x1)(x2)(x3)(xn)¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯为九位数的时候进行一次check并更新答案。

#include<bits/stdc++.h>using namespace std;long long maxx,x;void check(){    int num[10]={0},cnt=0,tmp=x;    num[0]=1;    while(tmp){        if (num[tmp%10]==0) cnt++;        else                return;        num[tmp%10]=1;        tmp/=10;    }    if (cnt==9){        maxx=max(maxx,x);    }}int main(){    for (int i=1;i<=9999;i++){        x=0;        for (int j=1,tmp,mul=1;j<=9;j++){            tmp=i*j;            while((x*mul+tmp)/mul!=x)   mul*=10;            x=x*mul+tmp;            if (x>1e9)  break;            check();        }    }    cout<<maxx<<endl;    return 0;}



第39题

这里写图片描述
题目来源ProjectEuler

当一个直角三角形周长p1000的时候,求p使得可能的直角三角形最多。
如果枚举所有p以及两条直角边,检查是否满足题意,这是可行的,但这个是O(n3)的算法,并不足够优秀。
考虑所有的直角三角形,可将他们划分为两类:1.三边互质的直角三角形;2.三边具有一个非1公因子的直角三角形。
显然,第二类直角三角形的三条边除以其最大公因子即可得到第一类直角三角形。我们称其为本原勾股数。
我们定义一对互质的数(s,t)s>t,则a=s2t2,b=st,c=s2+t2,则三元组(a,b,c)构成一组本原勾股数。具体的证明可参考http://www.cnblogs.com/devymex/archive/2010/08/07/1799713.html

我们每得到一组本原勾股数(a,b,c),则将num[a+b+c]++,此处复杂度O(nlogn)最后枚举所有长度求和,复杂度O(n2),这里的复杂度应该可以更低,我还没有仔细想过。

#include<bits/stdc++.h>using namespace std;const int maxp=1000;int num[1001];int gcd(int a,int b){return b==0? a:gcd(b,a%b);}int main(){    for (int i=1;i<=500;i++){        for (int j=1;j<i&&2*i*j+2*i*i<=1000;j++){            if (gcd(i,j)==1){                num[2*i*j+2*i*i]++;            }        }    }    int p=1000,maxn=0,ans;    for(int i=1;i<=p;i++){        int tmp=0;        for (int j=1;j<=p;j++){            if (i%j==0){                tmp+=num[j];            }        }        if (tmp>=maxn){            ans=i;maxn=tmp;        }    }    cout<<ans<<endl;    return 0;}



第40题

这里写图片描述
题目来源ProjectEuler

这题将[1,+)所有的数如图所示依次写下来。di表示第i个数字,求如题所示式子的结果。
第一种方法,从小到大枚举每一个数,记录当前所有数的长度,记录题中所求位置的数字,乘起来。复杂度O(nlogn)
当然我们可以有更快的方法,也就是第二种。
我们很容易算出小于10x的所有数的长度tot[x],则tot[x]=tot[x1]+x(10x10x1)
然后我们对于题中的每一个数aim,依次进行二分查找位置,当当前查找区间为[l,r]查找值为mid时
对于len[mid1]aim的情况,我们查找[l,mid]
此外对于len[mid]aim的情况,答案一定在mid这个数中,我们利用len[mid]和aim的差值,求出那个值
对于其他情况,我们查找[mid+1,r]
整体复杂度O(log3n)

#include<bits/stdc++.h>using namespace std;int tot[10],num[10];int search(int aim,int l,int r){    int mid=(l+r)/2;    int len=0;    while(mid){mid/=10;len++;}    mid=(l+r)/2;    if ((mid-num[len-1])*len+tot[len-1]-len>=aim)        return search(aim,l,mid);    else if ((mid-num[len-1])*len+tot[len-1]>=aim){        int diff=(mid-num[len-1])*len+tot[len-1]-aim;        while(diff){            diff--;            mid/=10;        }        return mid%10;    }    else        return search(aim,mid+1,r);}int main(){    tot[1]=9;num[1]=9;num[0]=0;    for (int i=2;i<=7;i++){        num[i]=(num[i-1])*10+9;        tot[i]=tot[i-1]+i*(num[i]-num[i-1]);    }    int ans=1;    for (int x=1,i=1;i<=7;i++){        ans*=search(x,1,1000000);        x*=10;    }    cout<<ans<<endl;    return 0;}
原创粉丝点击