Project Euler 03


OVERVIEW

  • Project Euler 03
    • 1.E22:Name score
    • 2.E32:Pandigital products
    • 3.E33:Digit cancelling fractions
    • 4.E36:Double-base palindromes
    • 5.E30:Digit fifth powers
    • 6.onlineJ599:两数之和
      • (1)暴力法:
      • (2)二分法:
      • (3)双指针法:
      • (4)哈希表:
      • (5)问题总结:
    • 7.onlineJ600:杨氏矩阵
      • (1)暴力法:
      • (2)值比较法:

1.E22:Name score

Using names.txt , a 46K text file containing over five-thousand first names,

begin by sorting it into alphabetical order.Then working out the alphabetical value for each name,

multiply this value by its alphabetical position in the list to obtain a name score.

For example, when the list is sorted into alphabetical order, COLIN,

which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list.

So, COLIN would obtain a score of 938 × 53 = 49714.

What is the total of all the name scores in the file?


在文本文件names.txt 中包含了五千多个名字,

首先将它们按照字母序排列,然后计算出每个名字的字母价值,乘以它在按字母顺序排列后的位置,就算出了这个名字的得分。

例如,按照字母序排列后,位于第938位的名字是COLIN,它的字母价值是3 + 15 + 12 + 9 + 14 = 53

因此,COLIN这个名字的得分是 938 × 53 = 49714.

上述文本文件中,所有名字的得分之和是多少?

算法思路:暴力枚举

  1. 将name.txt文件下载下来,进行初步处理%s/","/ /g(将连接符替换为空格分隔)、%s/"/ /g(处理开头结尾的单引号)
  2. 将名字输入保存到name[6005]数组中
  3. 首先使用sort算法对name数组进行排序
  4. 外循环遍历name数字中的每一个名字,内循环遍历name的每一个字母计算每个名字的价值value
  5. 输出最后累加的score分数
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;string name[6005];
int n;int main(){while(cin >> name[n]){n++;}sort(name, name + n);long long score = 0;for(int i = 0; i < n; ++i){int value = 0;//name的价值for(int j = 0; j < name[i].size(); ++j){value += name[i][j] - 'A' + 1;}score += value * (i + 1);}cout << score << endl;return 0;
}

补充:txt.name文件重定向输入操作./a.out < input22

2.E32:Pandigital products

We shall say that an n-digit number is pandigital if it makes use of all the digits 1 to n exactly once

For example, the 5-digit number, 15234, is 1 through 5 pandigital.

The product 7254 is unusual, as the identity, 39 × 186 = 7254,

containing multiplicand, multiplier, and product is 1 through 9 pandigital.

Find the sum of all products whose multiplicand/multiplier/product identity can be written as a 1 through 9 pandigital.

HINT: Some products can be obtained in more than one way so be sure to only include it once in your sum.


如果一个n位数包含了1至n的所有数字恰好一次,我们称它为全数字的;例如,五位数15234就是1至5全数字的。

7254是一个特殊的乘积,因为在等式39 × 186 = 7254中,被乘数、乘数和乘积恰好是1至9全数字的。

找出所有被乘数、乘数和乘积恰好是1至9全数字的乘法等式,并求出这些等式中乘积的和。

注意:有些乘积可能从多个乘法等式中得到,但在求和的时候只计算一次。(需要对乘积去重)

算法思路:暴力枚举

两层for循环不断尝试乘数与被乘数(乘积确定),动态判定结果是否符合全数字要求、乘积是否重复出现,符合则计入ans

  1. 枚举的范围:

    外层for循环范围,至少应从2开始(若为1乘数与成积重复必不为全数字),至多到98结束(3位数乘积将达到6位数必不符合)

    内层for循环范围,应该从j=i+1开始(乘数与被乘数重复),可暂时设置为死循环(在循环体中当iji*j位数和大于9结束)

  2. 对于全数字的判断

    开辟一个mark[10]标记数组,将被判断的数字拆分存入标记数组中,查看当遍历结束时标记数组中是否全为1(如果满足则+=ans

  3. 对于乘积相同的等式重复判断

    开辟一个check[10005]标记数组,如果乘积第1次出现则计入累加,否则乘积重复不记入

#include<iostream>
#include<cmath>
using namespace std;//1.求数字位数
int digit(int x){return floor(log10(x)) + 1;
}//2.检验每一位数字是否重复
int work(int *mark, int num){while(num){int temp = num % 10;//取num的最后一位if(mark[temp] == 1){return 0;//如果数字曾经出现过则直接return 0}mark[temp] = 1;//数字没有出现过将其标记值设为1num /= 10;}return 1;
}//3.检查数字是否为全数字
int func(int x, int y, int z){int mark[10] = {0};//mark为标记数组,用于检查数字是否重复if(work(mark, x) && work(mark, y) && work(mark, z) && mark[0] == 0){return 1;//如果3个数字都不冲突,且数字当中没有0则返回1}return 0;
}int main(){int ans = 0, check[10005] = {0};for(int i = 2; i < 99; ++i){for(int j = i + 1; 1; ++j){int a = digit(i), b = digit(j), c = digit(i * j);if(a + b + c > 9){//1.a+b+c>9不可能是答案直接退出循环break;}if(a + b + c == 9){//2.a+b+c==9对数字进行进一步验证if(func(i, j, i * j)){//利用func检查是否符合全数字cout << i << " " << j << " = " << i * j << endl;if(check[i * j] == 0){//利用check进行重复检查check[i * j] = 1;ans += i * j;}}}}}cout << ans << endl;return 0;
}

总结感悟:对于枚举法最重要的是确定枚举的范围,以及符合条件的结果判断

3.E33:Digit cancelling fractions

The fraction 49/98 is a curious fraction,

as an inexperienced mathematician in attempting to simplify it may incorrectly believe that 49/98 = 4/8,

which is correct, is obtained by cancelling the 9s.

We shall consider fractions like, 30/50 = 3/5, to be trivial examples.

There are exactly four non-trivial examples of this type of fraction,

less than one in value, and containing two digits in the numerator and denominator.

If the product of these four fractions is given in its lowest common terms, find the value of the denominator.


49/98是一个有趣的分数,缺乏经验的数学家可能在约简时错误地认为等式49/98 = 4/8

之所以成立是因为在分数线上下同时抹除了9的缘故。

我们也会想到,存在诸如30/50 = 3/5这样的平凡解。

这类有趣的分数恰好有四个非平凡的例子,它们的分数值小于1,且分子和分母都是两位数。

将这四个分数的乘积写成最简分数,求此时分母的值。

算法思路:暴力枚举

  1. 枚举范围:外层for循环范围11~99,内层for循环范围i+1-99
  2. 对于非凡解的判定:将约去同一数字后的分数与约去前的分数交叉相乘,若相等则为非凡解(对于平凡解的去除,任一数字不为0)
#include<iostream>
using namespace std;//1.判断分数是否为不平凡解
int func(int x, int y){int x1 = x / 10, x2 = x % 10, y1 = y / 10, y2 = y % 10;//将两位数进行拆解if(!x1 || !x2 || !y1 || !y2){return 0;//如果数字中含有0则一定为平凡解return 0}if(x1 == y1 && x * y2 == y * x2) return 1;if(x1 == y2 && x * y1 == y * x2) return 1;if(x2 == y1 && x * y2 == y * x1) return 1;if(x2 == y2 && x * y1 == y * x1) return 1;return 0;
}//2.辗转相除求a与b的最大公约数
int gcd(int a, int b){if(b == 0){return a;}return gcd(b, a % b);
}int main(){int a = 1, b = 1;for(int i = 11; i < 100; ++i){for(int j = i + 1; j < 100; ++j){if(func(i, j)){//如果j\i为不平凡的解,则打印例子cout << i << " / " << j << endl;a *= i;b *= j;}}}int temp = gcd(a, b);//temp为a与b的最大公约数a /= temp;b /= temp;cout << a << " / " << b << endl;return 0;
}

注意:熟练辗转相除法求最大公因数

4.E36:Double-base palindromes

The decimal number, 585 = 10010010012 (binary), is palindromic in both bases.

Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2.

(Please note that the palindromic number, in either base, may not include leading zeros.)


十进制数585 = 10010010012(二进制表示),因此它在这两种进制下都是回文数。

找出所有小于一百万,且在十进制和二进制下均为回文的数,并求它们的和。

(请注意无论在哪种进制下,回文数均不考虑前导零。)

算法思路:暴力枚举

#include<iostream>
using namespace std;//判断是否为回文数
int func(int num, int x){int raw = num;int temp = 0;//最后的temp值为回文数while(num){temp = temp * x + num % x;num /= x;}return temp == raw;
}int main(){int ans = 0;for(int i = 1; i < 1000000; ++i){if(func(i, 10) && func(i, 2)){cout << i << endl;ans += i;}}cout << ans << endl;return 0;
}

5.E30:Digit fifth powers

Surprisingly there are only three numbers that can be written as the sum of fourth powers of their digits:

1634 = 1^4 + 6^4 + 3^4 + 4^4
8208 = 8^4 + 2^4 + 0^4 + 8^4
9474 = 9^4 + 4^4 + 7^4 + 4^4

As 1 = 1^4 is not a sum it is not included.

The sum of these numbers is 1634 + 8208 + 9474 = 19316.

Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.


令人惊讶的是,只有三个数可以写成其各位数字的四次幂之和:

由于1 = 1^4 并不是求和,所以这里不计入内。

上面这三个数的和是1634 + 8208 + 9474 = 19316

找出所有可以写成其各位数字的五次幂之和的数,并求这些数的和。

算法思路:暴力枚举

  1. 枚举范围如何确定?

    首先考虑时间效率优化,对于1-9的五次幂可以先计算存储到一个数组中(避免每次重复计算,提升时间效率)

    利用关系(该数字 = 其各位数字的五次幂之和)确定枚举范围,

    假设该数字的位数为n,则有该数字取值为[1-10n),五次幂之和的取值范围为[n * 05, n * 9n]

    故另10n = n * 9n则可以解出该数字位数n的最大值,从而确定枚举范围(n的取值大致为5~6之间,枚举范围取6)

  2. 符合要求答案的条件:各位数字的五次幂之和等于该数字,则符合题目条件进行累加

#include<iostream>
using namespace std;int penta[15];void init(){for(int i = 1; i < 10; ++i){int temp = 1;for(int j = 0; j < 5; ++j){temp *= i;}penta[i] = temp;cout << i << ":" << penta[i] << " " << endl;}
}int func(int num){int raw = num;int temp = 0;while(num){temp += penta[num % 10];num /= 10;}return temp == raw;
}int main(){init();int ans = 0;for(int i = 2; i < 1000000; ++i){if(func(i)){ans += i;cout << i << endl;}}cout << ans << endl;return 0;
}

相似题:E34-Digit factorials

刷题总结:对于数字num进行各位5次幂求和的方法

int penta[15];
void init(){for(int i = 1; i < 10; ++i){int temp = 1;for(int j = 0; j < 5; ++j){temp *= i;}penta[i] = temp;cout << i << ":" << penta[i] << " " << endl;}
}
//与回文数字判断代码相似
int func(int num){int raw = num;int temp = 0;while(num){temp += penta[num % 10];num /= 10;}return temp == raw;
}

6.onlineJ599:两数之和

题目描述

给定一个从小到大的数组和一个目标数target,在其中找到两个数,使得两数之和与目标数相等,输出两个数在数组中的位置。

输入

第一行输入两个整数 n, target(1 ≤ n ≤ 1000000, 1 ≤ t ≤ 20000000)

接下来一行 n 个数,均小于10,000,000

输出

输出两个用空格隔开的数表示位置(从零开始计数),答案有唯一解

6 15
1 5 6 7 10 26
1 4
(1)暴力法:
#include<iostream>
#include<cstdio>
using namespace std;int num[1000005];
int n, target;int main() {scanf("%d%d", &n, &target);for (int i = 0; i < n; ++i) {scanf("%d", &num[i]);}for (int i = 0; i < n; ++i) {for (int j = i + 1; j < n; ++j) {if (num[i] + num[j] == target) {cout << i << " " << j << endl;return 0;}}}cout << "not fimd" << endl;return 0;
}

注意:由于数据范围(1≤n≤1000000, 1≤t≤20000000),暴力枚举的时间复杂度为O(n^2)一定会超时,空间复杂度为O(1)

(2)二分法:

思路:

  1. 使用for循环遍历数组中的每一个数字
  2. 在循环体内部,使用target值减去当前遍历的数字值(结果为配对值),在数组中二分查找该数字
  3. 如果找到配对值则输入答案,否则没有配对值输出nofind

复杂度分析:使用二分查找程序的时间复杂为O(nlogn)、空间复杂度为O(1)

#include<iostream>
#include<cstdio>
using namespace std;int num[1000005];
int n, target;int main(){scanf("%d%d", &n, &target);for(int i = 0; i < n; ++i){scanf("%d", &num[i]);}for(int i = 0; i < n; ++i){int temp = target - num[i];int l = i + 1, r = n - 1;while(l <= r){int mid = (l + r) / 2;if(temp == num[mid]){cout << i << " " << mid << endl;return 0;}if(temp < num[mid]){r = mid - 1;}else{l = mid + 1;}}}cout << "not fimd" << endl;return 0;
}
(3)双指针法:

思路:

  1. 分别初始化两个指针,指向数组的起始和结尾
  2. 将数组头尾元素相加sum值与target值比较:
    如果sum>target(数据偏大),则将右指针向左移动一位
    如果sum<target(数据偏小),则将左指针向右移动一位
    如果sum==target(找到结果),直接输出左右指针
  3. 当左右指针错开时,target查找结束(数组遍历完成没有找到结果nofind)

复杂度分析:使用双指针法的时间复杂度为O(n),空间复杂度为O(1),是一种效率比较高的解法。

#include<iostream>
#include<cstdio>
using namespace std;int num[1000005];
int n, target;int main(){scanf("%d%d", &n, &target);for(int i = 0; i < n; ++i){scanf("%d", &num[i]);}int l = 0, r = n - 1;while(l < r){int t = num[l] + num[r];if(t == target){printf("%d %d\n", l, r);return 0;}if(t > target){r--;}else{l++;}}printf("no find\n");return 0;
}
(4)哈希表:
#include<iostream>
using namespace std;int n, target;
int num[1000005], mark[1000005];int main() {scanf("%d%d", &n, &target);for (int i = 1; i <= n; ++i) {scanf("%d", &num[i]);mark[num[i]] = i;}for (int i = 1; i <= n; ++i) {int temp = target - num[i];if (mark[temp] != 0) {printf("%d %d\n", i - 1, mark[temp] - 1);return 0;}}printf("no find!\n");return 0;
}

注意:时间复杂度为O(n)、空间复杂度为O(n)

(5)问题总结:

两数之和问题,四种解法时空复杂分析,

在不限空间的情况下,最快的方法是哈希表法时间复杂度为O(n)

7.onlineJ600:杨氏矩阵

题目描述

给定一个n行m列的二维矩阵和一个目标数t,

二维矩阵中对于每一行从左到右不下降(右边的数≥左边的数),对于每一列从上到下不下降(下边的数≥上边的数)。

现要在数组中找到目标数t,并输出其所在位置的行数和列数。

输入

第一行两个整数n, m(1 ≤ n, m ≤ 3000)

接下来输入一个二维矩阵,矩阵内所有数均小于 200000。

最后输入一个整数t(1 ≤ t ≤ 200000)

输出

输出用空格隔开的数表示位置(从1开始计数),答案有唯一解。

3 4
1 2 3 4
5 6 15 20
7 10 20 25
15
2 3
(1)暴力法:

思路:

使用两个for循环对矩阵进行遍历操作,当遍历到target值时输出其行、列的下标值。

#include<iostream>
using namespace std;int n, m, target;
int num[3005][3005];int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {scanf("%d", &num[i][j]);}}scanf("%d", &target);for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {if (num[i][j] == target) {printf("%d %d\n", i, j);return 0;}}}printf("no find!\n");return 0;
}

注意:该暴力枚举法的时间复杂度为O(n * m),空间复杂度为O(1)

(2)值比较法:

思路:

由于矩阵数据的行、列递增性,可以巧取比较法实现target值的寻找:

  1. 从矩阵的左下角or右上角出发,进行对target值的依次比较(以从左下角出发为例)
  2. 如果target值比当前位置的数值大,则向右移动一格(行具有递增性)
  3. 如果target值比当前位置的数值小,则向上移动一格(列具有递减性)
  4. 经过多次移动后,当最后值等于target值时,输出行、列的下标

补充:如果最后的值移动出了矩阵范围,则表示没有找到target值

#include<iostream>
using namespace std;int n, m, target;
int num[3005][3005];int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {scanf("%d", &num[i][j]);}}scanf("%d", &target);int x = n, y = 1;//从左下角开始比较while (x > 0 && y <= m) {int cur = num[x][y];if (cur == target) {printf("%d %d\n", x, y);return 0;}if (cur > target) {--x;} else {++y;}}printf("no find!\n");return 0;
}

注意:该方法的时间复杂度为O(n + m),空间复杂度为O(1)

【AlgorithmTraining】03:Project Euler 03相关推荐

  1. 【AlgorithmTraining】08:Leetcode(上)

    Group1 1.LeetCode01 两数之和 给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标. 你可以假设每种输入 ...

  2. 【AlgorithmTraining】11:OnlineJ(下)

    OnlineJ(下) OVERVIEW OnlineJ(下) 1.OnlineJ503:独木舟(贪心) 参考代码 2.OnlineJ504:删数(贪心) 参考代码 3.OnlineJ505:最大整数( ...

  3. 【OpenCV-Python】教程:5-3 光流

    OpenCV Python Optical Flow (光流) [目标] 了解光流的概念和用 Lucas-Kanade 算法估计光流 我们将使用cv2.calcOpticalFlowPyrLK()这样 ...

  4. c语言字符串筛选字母,【程序设计】  功能:输入一个字符串,过滤此串,只保留串中的字母字 符,并统计新生成串中包含的字母个数...

    [程序设计] 功能:输入一个字符串,过滤此串,只保留串中的字母字 符,并统计新生成串中包含的字母个数0 mlfaxk2013.03.24浏览465次分享举报 求C语言高手解救!!!!!!哪里错了?#i ...

  5. 【jenkins】创建一个project基础配置、自动化邮件发送、pytest_terminal_summary收集结果

    [jenkins]创建一个project基础配置 一. 源码管理:以git仓库添加为例 二.Build Steps:以python为例 问题一:jenkins构件python脚本后,报告无法在jenk ...

  6. 【原创】Xilinx:K7 DDR3 IP核配置教程

    [原创]Xilinx:K7 DDR3 IP核配置教程 本文为明德扬原创文章,转载请注明出处! MIG IP控制器是Xilinx为用户提供的一个用于DDR控制的IP核,方便用户在即使不了解DDR的控制和 ...

  7. 【Python】dill:解决python的“AttributeError: Can‘t pickle local object”及无法pickle lambda函数的问题

    [Python]dill:解决python的"AttributeError: Can't pickle local object"及无法pickle lambda函数的问题 pyt ...

  8. 【转】NG:垂枝桦基因组图谱构建(2+3组装)及重测序分析

    [转]NG:垂枝桦基因组图谱构建(2+3组装)及重测序分析 转自希望组公众号.学习二代+三代组装策略的流程 垂枝桦(Betula pendula)是一种速生乔木,能在短短一年时间内开花,木质坚实,可做 ...

  9. 【NLP】GPT:第一个引入Transformer的预训练模型

    目前两种最重要的预训练语言模型,一种是前面介绍过的BERT,另外一种就是GPT. GPT出了两个版本,GPT1.0和GPT2.0,GPT2.0相对于GPT1.0差别不大,可以认为是它的增强版.本篇介绍 ...

最新文章

  1. ML之xgboost:基于xgboost(5f-CrVa)算法对HiggsBoson数据集(Kaggle竞赛)训练(模型保存+可视化)实现二分类预测
  2. 没有返回值的方法mock怎么写_【方法】小学生怎么写读书笔记?
  3. .net Api 接口调用 增删改查
  4. 如何用git将本地文件放到github上
  5. [leetcode] 554. 砖墙
  6. iOS学习笔记32 - 锚点
  7. Android找工作系列之事件传递机制
  8. ny17 单调递增最长子序列
  9. java编程中的点_java编程中所遇到的知识点(持续更新)
  10. PCB Layout的设计要点
  11. 三星2610打印机故障INTERNAL ERROR - Incomplete Session by time out
  12. 字节跳动Java大数据工程师面试题、笔试题(含答案)
  13. Layui的下拉框样式失效问题
  14. 当你灵感枯竭的时候,如何深挖客户需求?采用曼陀罗思考法(5W1H模式),相信你会找到出路
  15. 二代测序方法:DNA测序之靶向重测序
  16. 360主机卫士linux安装软件,360主机卫士Linux专杀版0.4.1 官方版(32位+64位)下载_云间下载...
  17. 大型网站架构技术演进(史上最全)
  18. 爱快路由器 默认ssh 管理用户名 sshd
  19. 【MATLAB】动态绘制曲线图(二维曲线)
  20. 阿里面试官问我:如何设计秒杀系统?我给出接近满分的回答

热门文章

  1. 云端软件平台 自己封装软件 图标不正常怎么办
  2. 小米2018年财报:雷军实现了承诺,但输掉了10亿赌局...
  3. 学习NLP的第14天——语言模型的训练与预测
  4. 站在中间立场看 360 VS QQ
  5. keil C51 中使用虚拟串口调试串口
  6. 高通功耗问题分析手段
  7. 近5年安徽省高考状元名单,全部被北大清华录取
  8. 图片上传出现“Invalid argument to native writeImage”异常或者上传图片变色
  9. 找出让硬盘狂转的元凶
  10. 也许一个人直到实现梦想那一刻