编程题欧拉

Project Euler

part 1

Program 1

3或5的倍数

在小于10的自然数中,35的倍数有3、5、69,这些数之和是23。求小于1000的自然数中所有35的倍数之和。

//解法一:暴力解法
#include <iostream>
using namespace std;
int main() {
    int ans = 0;
    for (int i = 3; i < 1000; i++) {
        if (i % 3 == 0 || i % 5 == 0) {
            ans += i;
        }
    }
    cout << ans << endl;
    return 0;
}
//该算法时间复杂度为O(n),空间复杂度为O(1)。
//解法二:算法优化,可以先计算出n以内3的倍数之和,1000内3的倍数依次为3~999,为等差数列,
//项数为999/3=333,采用等差数列求和公式,(3+999)*333/2;同理也可以计算出n以内5的倍数
//之和,两者和相加减去n以内15的倍数之和即为答案。
#include <iostream>
using namespace std;
int main() {
	int a3 = (3 + 999) * 333 / 2;
    int a5 = (5 + 995) * 199 / 2;
    int a15 = (15 + 990) * 66 / 2;
    cout << a3 + a5 -a15 <<endl;
    return 0;
}
//该算法时间复杂度为O(1),空间复杂度为O(1)。

Program 2

偶斐波那契数

斐波那契数列中的每一项都是前两项的和。由12开始生成的斐波那契数列的前10项为:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …

考虑该斐波那契数列中不超过四百万的项,求其中为偶数的项之和。

//解法一:暴力解法
#include <iostream>
using namespace std;
int num[1000005];//用于存储斐波那契数列
int main() {
    num[1] = 1, num[2] = 2;
    for(int i = 3; 1; i++){
        num[i] = num[i-1] + num[i-2];
        if(num[i] >= 4000000){
            break;
        }
    }
    long long ans = 0;//防止数据溢出
    for(int i == 1; 1; i++){
        if(num[i] % 2 == 0){
            ans += num[i];
        }
        if(num[i] >= 4000000){
            break;
        }
    }
    cout << ans << endl;
    return 0;
}
//该算法时间复杂度为O(n),空间复杂度为O(m)。
//解法二:只需求出斐波那契数列中不超过四百万偶数项之和,
//当我们计算到4000000项附近项数时,前面两项数组存储的数
//字对计算结果无意义,因此可以用两个变量来回倒腾,注释掉
//大数组,减小空间复杂度。
#include <iostream>
using namespace std;
//int num[1000005];
int main(){
    int a = 1, b = 2, ans = 0;
    while(b <= 4000000){
        if(b % 2 == 0){
            ans += b;
        }
        b = a + b;//将后面的数字更新至b变量
        a = b - a;//将原先b值赋给a
    }
    cout << ans << endl;
    return 0;
}
//该算法时间复杂度为O(n),空间复杂度为O(1)。

Program 4

最大回文乘积

回文数就是从前往后读和从后往前读都一样的数。由两个2位数相乘得到的最大的回文数是

9009 = 91 * 99

求由两个3位数相乘得到的最大的回文数。

//暴力解法:用两个for循环枚举两个3位数,并判断其乘积是否为最大回文数
#include <iostream>
using namespace std;
int func(int x){
	int rev = 0, raw = x;
	while(x){
		rev = rev * 10 + x % 10;
		x /= 10;
	}
	return rev == raw;
}
int main(){
    int ans = 0, a, b;
	for(int i = 100; i < 1000; i++){
		for(int j = i; j < 1000; j++){
			int t = i * j;
			if(func(t) && ans < t){
				ans = t;
				a = i, b = j;
			}
		}
	}
	cout << a << " " << b << endl;
	cout << ans <<endl;
    return 0;
}

Program 6

平方的和与和的平方

前十个自然数的平方的和是

 1^2 + 2^2 + ... + 10^2 = 385

前十个自然数的和的平方是

(1 + 2 + 3 + ... + 10)^2 = 55^2 = 3025

因此,前十个数的平方的和与和的平方之差是3025 - 385 = 2640

求前一百个数的平方的和与和的平方之差。

#include <iostream>
using namespace std;

int main(){
	int psum = 0, sump = 0;
	for(int i = 1; i <= 100; i++){
		psum += i * i;
		sump += i;
	}
	sump *= sump;
	cout << sump - psum << endl;
	return 0;
}

Program 8

连续数字最大乘积

在如下的1000位数中,连续四个数字的最大乘积是9 x 9 x 8 x 9 = 5832

73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450

求这个1000位数中连续十三个数字的最大乘积。

//在命令模式下,输入%s/ //g 然后输入重定向 ./a.out < input8
#include <iostream>
using namespace std;

int main(){
	char num[1005];
	cin >> num;
	long long ans = 0, now = 1, cnt = 0;
	for(int i = 0; i < 1000; i++){
		if(i < 13){
			now *= num[i] - '0';
		} else{
			if(num[i] == '0'){
				cnt++;
			} else{
				now *= num[i] - '0';
			}
			if(num[i - 13] == '0'){
				cnt--;
			}else{
				now /= num[i - 13] - '0';
			}
			if(cnt == 0){
				ans = max(ans, now);
			}
		}
	}
	cout << ans << endl;
	return 0;
}

Program 11

方阵中的最大乘积

在如下的20 x 20方阵中,有四个呈对角线排列的数被标记为红色。

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08

49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00

81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65

52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91

22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80

24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50

32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70

67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21

24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72

21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95

78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92

16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57

86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58

19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40

04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66

88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69

04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36

20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16

20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54

01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

这四个数的乘积是26 x 63 x 78 x 14 = 1788696
在这个20 x 20方阵中,四个呈一直线(竖直、水平或对角线)相邻的数的乘积最大是多少?

//方向数组 上右下左
//int dirx[4] = {-1, 0, 1,  0}
//int diry[4] = { 0, 1, 0, -1}
//int x, y; 坐标变换:x' = x + dirx[i];  y' = y + diry[i]; 
#include <iostream>
using namespace std;

int num[30][30], ans; //全局变量自动初始化为0
int dirx[4] = {-1, 0, 1, 1};
int diry[4] = {1, 1, 1, 0};

int main(){
	for(int i = 5; i < 25; i++){
		for(int j = 5; j < 25; j++){
			cin >> num[i][j];
		}
	}
	for(int i = 5; i < 25; i++){
		for(int j = 5; j < 25; j++){
			for(int k = 0; k < 4; k++){
				int t = num[i][j];
				for(int l = 1; l < 4; l++){
					int x = i + dirx[k] * l;
					int y = j + diry[k] * l;
					t *= num[x][y];
				}
				ans = max(ans, t);
			}
		}
	}
	cout << ans << endl;
	return 0;
}

Program 14

最长考拉兹序列

考虑如下定义在正整数集上的迭代规则:

n — n / 2(若为偶数) n — 3n + 1(若为奇数)

13开始,可以迭代生成如下的序列:
13 — 40 — 20 — 10 — 5 — 16 — 8 — 4 — 2 — 1

可以看出这个序列(从13开始到1结束)共有10项。尽管还未被证明,但普遍认为,从任何数开始最终都能回到1(这被称为“考拉兹猜想”)。

在小于一百万的数中,从哪个数开始迭代生成的序列最长?

注: 在迭代过程中允许出现超过一百万的项。

//递归法
#include <iostream>
using namespace std;
long long func(long long  n){
	if(n == 1){
		return 1;
	}
	if(n % 2 == 0){
		return func(n / 2) + 1;
	}
	return func(3 * n + 1) + 1;
}
int main(){
	long long ans = 1;
	for(int i = 1; i <= 1000000; i++){
		if(func(ans) < func(i)){
			ans = i;
		}
	}
	cout << ans << endl;
	return 0;
}
//fib递归
#include <iostream>
using namespace std;
int func(int n){
	if(n == 1) return 1;
    if(n == 2) return 2;
    return func(n - 1) + func(n - 2);
}
int main(){
	int n;
    cin >> n;
    cout << func(n) << endl;
	return 0;
}
//记忆化+递归约等于递推
#include <iostream>
using namespace std;
long long num[50];
long long func(int n){
	if(n == 1) return 1;
    if(n == 2) return 2;
    if(num[n]) return num[n];
    return num[n] = func(n - 1) + func(n - 2);
}
int main(){
	int n;
    cin >> n;
    cout << func(n) << endl;
	return 0;
}
//记忆化
#include <iostream>
using namespace std;
int num[10000000];
int func(long long n){
	if(n == 1){
		return 1;
	}
	if(n < 10000000 && num[n]){
		return num[n];
	}
	int t;
	if(n % 2 == 0){
		t = func(n / 2) + 1;
	} else{
		t = func(3 * n + 1) + 1;	
	}
	if(n < 10000000){
		num[n] = t;
	}
	return t; 
}
int main(){
	int ans = 1;
	for(int i = 2; i <= 1000000; i++){
		if(func(ans) < func(i)){
			ans = i;
		}
	}
	cout << ans << endl;
	return 0;
}

part2

Program 13

大和

求以下一百个50位数的和的前十位数字。

//大整数加法
#include <iostream>
#include <cstring>
using namespace std;
char num1[1005], num2[1005];
int n1[1005], n2[1005], ans[1005];
int main(){
    //输入处理数字的长度
    cin >> num1 >> num2;
    n1[0] = strlen(num1);
    n2[0] = strlen(num2);
    //确定答案的长度,为最长的一个
    ans[0] = max(n1[0], n2[0]);
    //分别处理两个加数,倒着存储
    for (int i = 1, j = n1[0] - 1; i <= n1[0]; i++, j--){
        n1[i] = num1[j] - '0';//输入时作为字符串存储,是个字符,将字符转数字
    }
    for (int i = 1, j = n2[0] - 1; i <= n2[0]; i++, j--){
        n2[i] = num2[j] - '0';
    }
    //处理加法
    for(int i = 1; i <= ans[0]; i++){
        ans[i] = n1[i] + n2[i];
    }
    //处理进位
    for(int i = 1; i <= ans[0]; i++){
        if(ans[i] > 9){
            ans[i + 1] += ans[i] / 10;
            ans[i] %= 10;
            if(i == ans[0]){
                ans[0]++;
            }
        }
    }
    for(int i = ans[0]; i > 0; i--){
        cout << ans[i];
    }
    cout << endl;
	return 0;
}
//将打整数加法抽象成函数做欧拉13题

part2

Program 25

1000位斐波那契数

斐波那契数列是按如下递归定义的数列:

因此斐波那契数列的前12项分别是:

第一个包含三位数字的是第12项。

在斐波那契数列中,第一个包含1000位数字的是第几项?

#include <iostream>
using namespace std;
int func(int *n1, int *n2){
	n2[0] = n1[0];
	for(int i = 1; i <= n2[0]; i++){
		n2[i] += n1[i];
		if(n2[i] > 9){
			n2[i + 1] += n2[i] / 10;
			n2[i] %= 10;
			if(i == n2[0]){
				n2[0]++;
			}
		}
	}
	return n2[0] >= 1000;
}
int main(){
	int num[2][1100] = {{1, 1}, {1, 1}};
	int a = 0, b = 1;
	for(int i = 3; 1; i++){
		if(func(num[a], num[b])){
			cout << i << endl;
			break;
		}
		swap(a, b);
	}
	return 0;
}

大整数乘法

//mul.cpp
#include <iostream>
#include <cstring>
using namespace std;
char num1[1005], num2[1005];
int n1[1005], n2[1005], ans[1005];
int main(){
    cin >> num1 >> num2;
    n1[0] = strlen(num1);
    n2[0] = strlen(num2);
    ans[0] = n1[0] + n2[0] -1;
    for(int i = n1[0], j = 0; i > 0; i--, j++){
        n1[i] = num1[j] - '0';
    }
    for(int i = n2[0], j = 0; i > 0; i--, j++){
        n2[i] = num2[j] - '0';
    }
    for(int i = 1; i <= n1[0]; i++){
        for(int j = 1; j <= n2[0]; j++){
            ans[i + j -1] += n1[i] * n2[j];
        }
    }
    for(int i = 1; i <= ans[0]; i++){
        if(ans[i] > 9){
            ans[i + 1] += ans[i] / 10;
            ans[i] %= 10;
            if(i == ans[0]){
                ans[0]++;
            }
        }
    }
    for(int i = ans[0]; i > 0; i--){
        cout << ans[i];
    }
    cout << endl;
    return 0;
}

Program 15

网格路径

从一个2 x 2网格的左上角出发,若只允许向右或向下移动,则恰好有6条抵达右下角的路径。

对于20 x 20网格,这样的路径有多少条?

#include <iostream>
using namespace std;
//int dp[25][25];
long long dp[25][25];
int main(){
	for(int i = 1; i <= 21; i++){
		for(int j = 1; j <= 21; j++){
			if(i == 1 && j == 1){
				dp[i][j] = 1;
			}else{
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
		}
	}
	cout << dp[21][21] << endl;
	return 0;
}

//第二种 40个位置选取20个,组合简化
#include <iostream>
using namespace std;
int main(){
	long long ans = 1;
	for(int i = 40, j = 1; i > 20; i--, j++){
		ans *= i;
		ans /= j;
	}
	cout << ans << endl;
	return 0;
}

Program 18

最大路径和 I

从如下的数字三角形的顶端出发,不断移动到下一行与其相邻的元素,所能得到的最大路径和是23。

3
7 4 
2 4 6 
8 5 9 3

如上图,最大路径和为3 + 7 + 4 + 9 = 23。
从如下的数字三角形顶端出发到达底部,求所能得到的最大路径和。

                      75
                     95 64
                   17 47 82
                 18 35 87 10
                20 04 82 47 65
              19 01 23 75 03 34
             88 02 77 73 07 63 67
            99 65 04 28 06 16 70 92
           41 41 26 56 83 40 80 70 33
         41 48 72 33 47 32 37 16 94 29
        53 71 44 65 25 43 91 52 97 51 14
      70 11 33 28 77 73 17 78 39 68 17 57
     91 71 52 38 17 14 91 43 58 50 27 29 48
    63 66 04 68 89 53 67 30 73 16 69 87 40 31
  04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

注意: 在这个问题中,由于只有16384条路径,通过穷举所有的路径来解决问题是可行的。但是,对于第67题,虽然是一道相同类型的题目,但是其中的数字三角形将拥有一百行,就不再能够通过暴力破解来解决,而需要一个更加聪明的办法!;o)

#include <iostream>
using namespace std;
int n, num[20][20];
int main(){
	n = 15;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			cin >> num[i][j];
		}
	}
	int ans = 0;
	for(int i = 2; i <= n; i++){
		for(int j = 1; j <= i; j++){
			num[i][j] += max(num[i - 1][j - 1], num[i - 1][j]);
			ans = max(ans, num[i][j]);
		}
	}
	cout << ans << endl;
	return 0;
}

//第二种
#include <iostream>
using namespace std;
int n, num[20][20];
int main(){
	n = 15;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			cin >> num[i][j];
		}
	}
	for(int i = n -1; i > 0; i--){
		for(int j = 1; j <= i; j++){
			num[i][j] += max(num[i + 1][j], num[i + 1][j + 1]);
		}
	}
	cout << num[1][1] << endl;	
	return 0;
}

OJ590

树塔狂想曲

题目描述

相信大家都学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j) 号点只能走向 (i+1,j) 或者 (i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。

1

3 8

2 5 0

1 4 3 8

1 4 2 5 0

路径最大和是 1+8+5+4+4=22,1+8+5+3+5=22 或者 1+8+0+8+5=22

S 觉得这个问题 so easy。于是他提高了点难度,他每次 ban 掉一个点(即规定哪个点不能经过),然后询问你不走该点的最大路径和。当然他上一个询问被 ban 掉的点过一个询问会恢复(即每次他在原图的基础上 ban 掉一个点,而不是永久化的修改)。

输入

第一行包括两个正整数 N,M 分别表示数塔的高和询问次数。

以下N行,第 i 行包括用空格隔开的 i−1 个数,描述一个高为 N 的数塔。

而后 M 行,每行包括两个数X,Y,表示第X 行第 Y 列的数塔上的点被小 S ban 掉,无法通行。

(由于读入数据较大,请使用较为快速的读入方式)

输出

M行每行包括一个非负整数,表示在原图的基础上 ban 掉一个点后的最大路径和,如果被 ban 掉后不存在任意一条路径,则输出 −1。

样例输入
5 3
1
3 8
2 5 0
1 4 3 8
1 4 2 5 0
2 2
5 4
1 1
样例输出
17
22
-1
样例说明
第一次:
1
3 X
2 5 0
1 4 3 8
1 4 2 5 0
1+3+5+4+4 = 17 或者 1+3+5+3+5=17
第二次:
1
3 8
2 5 0
1 4 3 8
1 4 2 X 0
1+8+5+4+4 = 22
第三次:无法通行,-1!
数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1 ≤ N ≤ 1000,1 ≤ M ≤ 5 ∗ 10^5

#include <iostream>
#include <cstdio>
using namespace std;
int n, m, num[1005][1005], utd[1005][1005], dtu[1005][1005], ans[1005][1005], mmax[1005], sec[1005];
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			scanf("%d", &num[i][j]);
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			utd[i][j] = max(utd[i - 1][j - 1], utd[i - 1][j]) + num[i][j];
		}
	}
	for(int i = n; i > 0; i--){
		for(int j = 1; j <= i; j++){
			dtu[i][j] = max(dtu[i + 1][j], dtu[i + 1][j + 1]) + num[i][j];
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			ans[i][j] = utd[i][j] + dtu[i][j] - num[i][j];
		}
	}
	for(int i = 2; i <= n; i++){
		int m1 = 0, m2 = 0,ind = 0;
		for(int j = 1; j <= n; j++){
			if(ans[i][j] > m1){
				m2 = m1;
				m1 = ans[i][j];
				ind = j;				
			}else if(ans[i][j] > m2){
				m2 = ans[i][j];
			}
		}
		mmax[i] = ind;
		sec[i] = m2;
	}
	for(int i = 0; i < m; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		if(x == 1){
			//cout << -1 << endl;
			printf("-1\n");
		}else if(y == mmax[x]){
			//cout << sec[x] << endl;
			printf("%d\n", sec[x]);
		}else{
			//cout<< dtu[1][1] << endl;
			printf("%d\n", dtu[1][1]);
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值