欧拉计划基础篇(一)

前言

本系列博文用于欧拉计划的刷题总结,同时也为访问欧拉原站不太顺畅的盆友提供部分便利。

欧拉原站:https://pe-cn.github.io/
可选择的评测oj:https://oj.haizeix.com/

About Project Euler(关于欧拉计划)

What is Project Euler?(什么是欧拉计划?)

Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.

欧拉计划是一系列有挑战性的数学与计算机编程题;要解开它们,需要的不止是数学知识:尽管数学能够帮助你找到一些优雅而有效的方法,大多数题目仍需要借助计算机和编程技巧来完成解答。

The motivation for starting Project Euler, and its continuation, is to provide a platform for the inquiring mind to delve into unfamiliar areas and learn new concepts in a fun and recreational context.

创立欧拉计划的初衷,以及不断维持其运行的动力,在于为好奇的头脑提供一个平台,使他们能够在有趣愉悦的氛围中,探索未知领域,学习新的知识。

Who are the problems aimed at?(这些题目的受众是谁?)

The intended audience include students for whom the basic curriculum is not feeding their hunger to learn, adults whose background was not primarily mathematics but had an interest in things mathematical, and professionals who want to keep their problem solving and mathematics on the cutting edge.

欧拉计划预期的受众,包括在基础课程外学有余力的学生、非数学背景但对数学感兴趣的成年人以及希望磨炼解题能力或是数学能力的专业人士。

Can anyone solve the problems?(所有人都能解开这些题目吗?)

The problems range in difficulty and for many the experience is inductive chain learning. That is, by solving one problem it will expose you to a new concept that allows you to undertake a previously inaccessible problem. So the determined participant will slowly but surely work his/her way through every problem.

欧拉计划的题目难度不一,对于大多数人来说,解题就是一个逐渐学习的过程。也就是说,每当你解开一个题目,你将会了解一些新的知识,从而帮助你着手解决以前无从下手的题目。因此,任何有决心的参与者,即使进展再缓慢,也一定能逐一解开每一道题。

“Project Euler exists to encourage, challenge, and develop the skills and enjoyment of anyone with an interest in the fascinating world of mathematics.”
“欧拉计划的存在,是为了每个对数学感兴趣的人,鼓励他们,挑战他们,并最终培养他们的能力与乐趣。”

EP-01

在这里插入图片描述
在这里插入图片描述

常规解法

逐个遍历0 - 1000,判断是否符合条件符合则加入即可(不包括1000)

  • 复杂度:O(n)

ans1:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include<cstring>
#include<cmath>

using namespace std;
//数学模拟
int main()
{
    int  sum = 0;
    for(int i = 1; i < 1000; i++){
        if(i % 3 == 0 || i % 5 == 0)
            sum += i;
    }

    cout << "1000以内3或5的倍数和为:" << sum;

return 0;
}

ans2:将性质判断封装

#include<stdio.h>
#define MAX_N 1000
int is_val(int n){
    if(n % 3 == 0 || n % 5 == 0)
        return 1;
    return 0;
}

int main(){
    int sum = 0;
    for(int i = 1; i < MAX_N; i++){
        if(is_val(i))
            sum += i;
    }
    printf("%d\n", sum);
    return 0;
}

此处is_val可简化:

int is_val(int n){
    return (n % 3 == 0 || n % 5 == 0);//符合是为真不符合为假
}
优化

可不使用循环,直接以等差数列求和公式将1000以内3和5的倍数相加,然后减去重复部分(即15的倍数)

  • 项数:999/15自动向下取整
  • 时间复杂度为O(1)——常数级
#include<stdio.h>
#include<math.h>
int main()
{
    int sum3, sum5, sum15;
    sum3 = (3 + 999) * 333 / 2;
    sum5 = (5 + 995) * (999 / 5) / 2;
    //错误:sum15 = (15 + 985) * (985 / 15) / 2;
    sum15 = (15 + 999 / 15 * 15) * (999 / 15) / 2;//sum15 = (15 + 990) * (985 / 15) / 2;
    printf("%d\n", sum3 + sum5 - sum15);

return 0;
}

EP-02

在这里插入图片描述
在这里插入图片描述

常规解法:

中文题目可能会有争议,偶数项是指项数还是值,可阅读英文原题发现明确指出为“values”

ans1:递归版

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include<cstring>
#include<cmath>
#define MAX_N 4'000'000
using namespace std;
long fib(int n){
    if(n == 1)  return 1;
    if(n == 2) return 2;
return fib(n - 1) + fib(n - 2);
}
int main()
{   int sum = 0;
    for(int i = 1; fib(i) <= MAX_N; i++)
        if(fib(i) % 2 == 0)
            sum += fib(i);
    cout << sum;
return 0;
}

ans2:数组版:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include<cstring>
#include<cmath>
#define MAX_N 4'000'000
using namespace std;

int main()
{   int n = 2, sum = 0;
    long fib[50] = {1, 1, 2};//将1作为虚拟的第0项,处理时将fib[1]作为第一项处理
    while(fib[n - 1] + fib[n - 2] < MAX_N){
        fib[n] = fib[n - 1] + fib[n - 2];
        if(fib[n] % 2 == 0) sum += fib[n];
        n++;
    }
    cout << sum <<endl;
return 0;
}
  • 递归写法优势在于写起来方便,思维量更小,但数组写法明显要快的多(递归太慢了主要是)
  • 但数组有些小问题,一在于不知道数组该开多大(当然可以通过再写程序测试或者vector等方法解决),二是这种看起来非常大的数据很容易有爆栈的担忧(即使本题实际上需要的数组容量很小)
优化一——滚动数组
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include<cstring>
#include<cmath>
#define MAX_N 4'000'000
//=优化一————滚动数组

using namespace std;

int main()
{   int sum = 2;
    int n = 2;
    int fib[3] = {1, 1, 2};
    while(fib[n % 3] + fib[(n - 1) % 3] <= MAX_N){//第一个新数即fib[3]也即第三项存在fib[0],那么fib(4)在fib[1],而fib(4)=fib(3)+fib(2)=fib[0]+fib[2]
        n += 1;//!必须在第一行,如果在最后一行,第一次执行时将不迭代(注意区别于基本数组版,它的n++在最后基于sum初始值为0[实际上第一仍然不迭代],两种处理皆可但本程序其实更符合逻辑
        fib[n % 3] = fib[(n - 1) % 3] + fib[(n - 2) % 3];//第一个新数存在0位,第二个存在1位,第三个在2位,n迭代时实际上将每次生成的数的存储位置都将后移
        if(fib[n % 3] % 2 == 0)//f[n%3]总是最新的数,只是岁n的改变在数组中的位置改变
            sum += fib[n % 3];
    }

cout << sum << endl;


return 0;
}

空间复杂优化为:O(1)——常数级

  • 注意这里有四百万的具体值所有上面为优化的程序实际也是O(1),但该优化无论临界值为多少都是常数级,而该问题抽象后上面为优化的陈旭为O(N)
优化一扩展:运算替换
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include<cstring>
#include<cmath>
#define MAX_N 4'000'000
//=优化一————滚动数组

using namespace std;

int main()
{   int sum = 2;
    int n = 2;
    int fib[3] = {1, 1, 2};
    while(fib[n % 3] + fib[(n - 1) % 3] <= MAX_N){//第一个新数即fib[3]也即第三项存在fib[0],那么fib(4)在fib[1],而fib(4)=fib(3)+fib(2)=fib[0]+fib[2]
        n += 1;//!必须在第一行,如果在最后一行,第一次执行时将不迭代(注意区别于基本数组版,它的n++在最后基于sum初始值为0[实际上第一仍然不迭代],两种处理皆可但本程序其实更符合逻辑
        fib[n % 3] = fib[(n - 1) % 3] + fib[(n - 2) % 3];//第一个新数存在0位,第二个存在1位,第三个在2位,n迭代时实际上将每次生成的数的存储位置都将后移
        if(!(fib[n % 3] & 1))//!将模2操作优化为按位与1(位运算为所有运算中最快的,而求余运算是数学运算中最慢的),等于0判断优化为取非(取非后为有余数即0即不满足)
            sum += fib[n % 3];//f[n%3]总是最新的数,只是岁n的改变在数组中的位置改变
    }

cout << sum << endl;
return 0;
}

  • 31
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值