文章目录
- 欧拉计划
- Problem01等差数列公式
- Problem02滚动数组求斐波那契数
- Problem03最大质因子
- Problem04最大回文乘积 回文数
- Problem05最小公倍数
- Problem06平方和公式
- Problem07线性筛
- Problem08滑动窗口(逆运算) 长字符串输入
- Problem09素勾股数
- Problem10素数和(线性筛)
- Problem11方向数组
- Problem12因子个数
- Problem13大整数加法 文件中输入
- Problem14考拉兹序列 记忆化
- Problem15排列组合公式
- Problem16数字的幂 大整数
- Problem17英文字母个数
- Problem18记忆化 动态规划 dfs
- Problem20阶乘数字和 大整数
- Problem21因子和
- Problem24 字典序全排列
- Problem25大整数斐波那契 滚动数组
- Problem26求循环节
- Problem28规律 螺旋数阵对角线
- Problem29 大整数去重
- Problem30各位数字的5次幂
- Problem31硬币求和 递推★★★
- Problem32求数字位数
- Problem33错误约分正确答案
- Problem34各位数字的阶乘和 记忆化
- Problem35圆环素数 位数
- Problem36双进制下回文数
- Problem37左截右截依旧是素数
- Problem38连接几个乘积为 全数字判断
- Problem39素勾股数的使用
- Problem44二分查找 五边形数
- Problem45六边形数中 二分查找 五边形数
- Problem46不一定奇合数=素数+平方2倍
- Problem47素因子种类
欧拉计划
Problem01等差数列公式
利用等差数列来做
#include<stdio.h>
int main() {
int sum3 = (3 + 999) * 333 / 2;
int sum5 = (5 + 995) * (995 / 5) / 2;
int sum15 = (15 + (999 / 15) * 15) * (999 / 15) / 2;
printf("%d\n", sum3 + sum5 - sum15);
return 0;
}
Problem02滚动数组求斐波那契数
每一项约为前一项的1.5倍
方法一:可以推导出400000大约在44项左右
#include <stdio.h>
#define MAX_N 44
#define N 4000000
int fib[MAX_N + 5];
int main() {
fib[1] = 1,
fib[2] = 2;
long long sum = 2;
for(int i = 3; i <= MAX_N && fib[i - 1] + fib[i - 2] <= N; ++i) {
fib[i] = fib[i - 1] + fib[i - 2];
if(fib[i] & 1) continue;
sum += fib[i];
}
printf("%lld\n", sum);
return 0;
}
方法二:
#include<stdio.h>
#define N 4000000
int main() {
int sum = 0, n = 0;
int a = 0, b = 1, c;
while(a + b <= N) {
n += 1;
b = a + b;
a = b - a;
if(b & 1) continue;
sum += b;
}
printf("n = %d, sum = %d\n", n, sum);
return 0;
}
方法三:滚动数组, 循环利用3个空间
F
[
i
%
3
]
=
F
[
(
i
−
1
)
%
3
]
+
F
[
(
i
−
2
)
%
3
]
等
价
于
F
[
i
]
=
F
[
i
−
1
]
+
F
[
i
−
2
]
F[i\%3] = F[(i -1)\%3] + F[(i - 2)\%3]\\等价于\\ F[i] = F[i-1] + F[i - 2]
F[i%3]=F[(i−1)%3]+F[(i−2)%3]等价于F[i]=F[i−1]+F[i−2]
#include <iostream>
using namespace std;
int main() {
int a[3]={0, 1, 2};
int sum = 0;
for(int i = 2; a[(i - 1) % 3] + a[(i - 2) % 3] <= 4000000; ++i) {
a[i % 3] = a[(i - 1) % 3] + a[(i - 2) % 3];
if(a[i % 3] % 2 == 0) sum += a[i % 3];
}
cout << sum;
return 0;
}
#include<stdio.h>
#define N 4000000
int main() {
int fib[3] = {0, 1};
int sum = 0, n = 2;
while(fib[(n - 1) % 3] + fib[(n - 2) % 3] <= N) {
fib[n % 3] = fib[(n - 1) % 3] + fib[(n - 2) % 3];
if(!(fib[n % 3] & 1)) sum += fib[n % 3];
n += 1;
}
printf("%d\n", sum);
return 0;
}
Problem03最大质因子
对于任意合数 n = a * b (a > 1, b > 1)
假设
a
<
=
b
a <= b
a<=b, 则
a
<
=
n
,
b
>
=
n
;
a <= \sqrt n, b >= \sqrt n;
a<=n,b>=n;
#include <stdio.h>
#define N 600851475143LL //LL识别长整型
int main() {
long long ans = 0, M = N;
long long i = 2;
while(i * i <= M) {
if(M % i == 0) ans = i;//ans一定是素因子
while(M % i == 0) M /= i;
i += 1;
}
//如果M是合数的话,在循环内一定会除到1
if(M > 1) ans = M; //此行的必要
printf("%lld\n", ans);
return 0;
}
Problem04最大回文乘积 回文数
#include <stdio.h>
int is_val(int n) {
int x = n;
int temp = 0;
while(x) {
temp = temp * 10 + x % 10;
x /= 10;
}
return temp == n;
}
int main() {
int ans = 0;
for(int i = 100; i < 1000; ++i) {
for(int j = i; j < 1000; ++j) {
if(is_val(i * j) && i * j > ans) ans = i * j;
}
}
printf("%d\n", ans);
return 0;
}
扩展:判断在base进制下是不是回文数的代码
#include <stdio.h>
int is_val(int n, int base) {
int x = n;
int temp = 0;
while(x) {
temp = temp * base + x % base;
x /= base;
}
return temp == n;
}
int main() {
int ans = 0;
for(int i = 100; i < 1000; ++i) {
for(int j = i; j < 1000; ++j) {
if(is_val(i * j) && i * j > ans) ans = i * j;
}
}
printf("%d\n", ans);
return 0;
}
Problem05最小公倍数
就是找1~20的最小公倍数
最小公倍数求解公式
l
c
m
(
a
,
b
)
=
a
∗
b
g
c
d
(
a
,
b
)
lcm(a, b) = \frac{a * b}{gcd(a, b)}
lcm(a,b)=gcd(a,b)a∗b
#include<stdio.h>
int gcd(int a, int b) {
return (b ? gcd(b, a % b) : a);
}
int main() {
int ans = 1;
for(int i = 2; i <= 20; ++i) {
//下面两行代码,求乘积找和下一个数的最小公倍数,乘积是下一个数的倍数时下一次循环
if(ans % i == 0) continue;
ans *= i / gcd(ans, i);
}
printf("%d\n", ans);
return 0;
}
Problem06平方和公式
平方和公式是一个比较常用公式,用于求连续自然数的平方和(Sum of squares),其和又可称为四角锥数,或金字塔数(square pyramidal number)也就是正方形数的级数。
此公式是冯哈伯公式(Faulhaber’s formula)的一个特例
扩展:
- 立方和 : a 3 + b 3 = ( a + b ) ( a 2 − a b + b 2 ) a^3 +b^3 = (a+b)(a^2-ab+b^2) a3+b3=(a+b)(a2−ab+b2)
- 立方差: a 3 − b 3 = ( a − b ) ( a 2 + a b + b 2 ) a^3 - b^3 = (a-b)(a^2+ab+b^2) a3−b3=(a−b)(a2+ab+b2)
- 立方和累加: 1 3 + 2 3 + . . . n 3 = [ n ( n + 1 ) 2 ] 1^3 + 2 ^3 + ...n^3 =[\frac{n(n+1) }{2}] 13+23+...n3=[2n(n+1)]
用三次方求平方,…
#include<stdio.h>
int main() {
int sum1 = 5050, sum2 = 100 * (100 + 1) * (2 * 100 + 1) / 6;
printf("%d\n", sum1 * sum1 - sum2);
return 0;
}
Problem07线性筛
#include <stdio.h>
#define max 200000
int prime[max + 5] = {0};
void init() {
for(int i = 2; i * i <= max; ++i) {
if(prime[i]) continue;
for(int j = i * i; j <= max; j += i) {
prime[j] = 1;
}
}
//将素数按从小到达顺序存起来,prime[0]记录素数个数
//下面一个循环也可以写上面筛选的外层循环中, 但需要注意外层循环的条件
//看下个代码
for(int i = 2; i <= max; ++i) {
if(!prime[i]) prime[++prime[0]] = i;
}
return ;
}
int main() {
init();
printf("%d\n", prime[10001]);
return 0;
}
#include <stdio.h>
#define maxn 200000
int prime[maxn + 5] = {0};
void init() {
for(int i = 2; i <= maxn; ++i) {
if(prime[i]) continue;
prime[++prime[0]] = i;
//prime[0]++;
//prime[prime[0]] = i;
for(int j = i * 2; j <= maxn; j += i) {
prime[j] = 1;
}
}
return ;
}
int main() {
init();
printf("%d\n", prime[10001]);
return 0;
}
线性筛代码
#include <stdio.h>
#define MAX 200000
int prime[MAX + 5] = {0};
void x_prime() {
for(int i = 2; i <= MAX; ++i) {
if(!prime[i]) prime[++prime[0]] = i;
for(int j = 1; j <= prime[0]; ++j) {
if(prime[j] * i > MAX) break;
prime[prime[j] * i] = 1;
if(i % prime[j] == 0) break; //判断当前素数是不是 j 中最小素数,是的话就跳出来
}
}
return ;
}
int main() {
x_prime();
printf("%d\n", prime[10001]);
return 0;
}
Problem08滑动窗口(逆运算) 长字符串输入
-
逆运算: a与b通过法则一得到c, c与a,b任意一个通过法则二得到另一个,则法则二是法则一的逆运算,否则不是。如除法是乘法的逆运算而乘法不是出发的逆运算
-
输入字符串的一个技巧
从文件读入数据具体看欧拉复习
#include <string.h>
#include <stdio.h>
#define max 1000
char num[max + 5];
int main() {
int len = 0;
while(~scanf("%s", num + len)) {
len = strlen(num);
printf("%s\n", num);
}
return 0;
}
运行结果
利用滑动窗口法作此题
8.h
#ifndef _8_H
#define _8_H
char s[10010] =
"73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450";
#endif
8.cpp
#include<cstdio>
#include <cstring>
#include<iostream>
#include "8.h"
using namespace std;
int main() {
long long MAX = 0, temp = 1, zero = 0;
for(int i = 0; s[i]; i++) {
if(i >= 13) {
if(s[i - 13] == '0') {
zero--;
} else {
temp /= (s[i - 13] - '0');
}
}
if(s[i] == '0') {
zero++;
} else {
temp *= (s[i] - '0');
}
if(zero == 0 && i >= 12 && temp > MAX) {
MAX = temp;
}
}
cout << MAX << endl;
return 0;
}
Problem09素勾股数
素勾股数: a, b, c三者互质,
a
2
+
b
2
=
c
2
a^2 + b^2 = c^2
a2+b2=c2, 有四个性质
- (na, nb, nc)也是勾股数
- (a, b, c)之间两两互质
- a, b必为一奇一偶
- 任何素勾股数均可有如下表示(其中 n < m, gcd(n, m) = 1)
a = 2 ∗ m ∗ n a = 2 * m * n a=2∗m∗n
b = m 2 − n 2 b = m^2 - n^2 b=m2−n2
c = m 2 + n 2 c = m^2 + n^2 c=m2+n2
代码:
#include<iostream>
using namespace std;
int gcd(int a, int b) {
return (b ? gcd(b, a % b) : a);
}
int main() {
int ans = 0;
for(int n = 1; n <= 33; ++n) {
for(int m = n + 1; m * m + n * n < 1000; m++) {
if(gcd(m, n) - 1) continue;
int a = 2 * m *n;
int b = m * m - n * n;
int c = m * m + n * n;
if(1000 % (a + b + c) == 0) {
int k = 1000 / (a + b + c);
ans = a * b * c * k * k * k;
}
if(ans) break;
}
if(ans) break;
}
cout << ans << endl;
return 0;
}
Problem10素数和(线性筛)
线性筛写
#include <stdio.h>
#define max 2000000
int a[max] = {0};
void Prime() {
for(int i = 2; i <= max; ++i) {
if(!a[i]) {
a[++a[0]] = i;
}
for(int j = 1; j <= a[0]; ++j) {
if(a[j] * i > max) break;
a[a[j] * i] = 1;
if(i % a[j] == 0) break;
}
}
return ;
}
int main() {
Prime();
long long ans = 0;
for(int i = 1; i <= a[0]; ++i) {
ans += a[i];
}
printf("%lld\n", ans);
return 0;
}
Problem11方向数组
方向数组:
/************************
重定向标准输入,将数据存入一个文件
*************************/
#include<iostream>
using namespace std;
int dirct[4][2] = {
0, 1,
1, 0,
1, 1,
-1, 1
};
int arr[30][30] = {0};
int main() {
int ans = 0;
for(int i = 5; i < 25; ++i) {
for(int j = 5; j < 25; ++j) {
cin >> arr[i][j];
}
}
for(int i = 5; i < 25; ++i) {
for(int j = 5; j < 25; ++j) {
for(int k = 0; k < 4; ++k) {
int temp = 1;
for(int l = 0; l < 4; ++l) {
int x = i + l * dirct[k][0];
int y = j + l * dirct[k][1];
temp *= arr[x][y];
}
if(temp > ans) ans = temp;
}
}
}
cout << ans << endl;
return 0;
}
Problem12因子个数
关键点
- n = n= n= p 1 a 1 p_1 ^ {a_1} p1a1 * p 2 a 2 ∗ p 3 a 3 . . . p n a n p_2 ^ {a_2}*p_3^{a_3}...p_n^{a_n} p2a2∗p3a3...pnan
则 n 的因子个数 f ( n ) = ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ ( a 3 + 1 ) . . . ( a n + 1 ) f(n) = (a_1 + 1) * (a_2 + 1) * (a_3 + 1)...(a_n + 1) f(n)=(a1+1)∗(a2+1)∗(a3+1)...(an+1)- 若A,B互质,则 f ( A ∗ B ) = f ( A ) ∗ f ( B ) f(A*B) = f(A) * f(B) f(A∗B)=f(A)∗f(B)
本题两种做法:试除法 和 线性筛来做
试除法在欧拉3求最大素因子时用到,从2开始除尽所有素因子
用线性筛最关键的一点是什么?
主要是第二点
线性筛原理是用一个数乘以素数来筛合数,
i
∗
p
r
i
m
e
[
j
]
i * prime[j]
i∗prime[j]为一个合数
那么
f
(
i
∗
p
r
i
m
e
[
j
]
)
=
f
(
i
)
∗
f
(
p
r
i
m
e
[
j
]
)
f(i * prime[j]) = f(i) * f(prime[j])
f(i∗prime[j])=f(i)∗f(prime[j])
因此在筛素数的过程中直接把因子个数再求出来
注意一点:
i
%
p
r
i
m
e
[
j
]
!
=
0
i\ \%\ prime[j]\ != 0
i % prime[j] !=0时,
i
i
i与
p
r
i
m
e
[
j
]
prime[j]
prime[j]互质,
f
(
i
∗
p
r
i
m
e
[
j
]
)
=
f
(
i
)
∗
f
(
p
r
i
m
e
[
j
]
)
f(i * prime[j]) = f(i) * f(prime[j])
f(i∗prime[j])=f(i)∗f(prime[j])
当筛到
i
%
p
r
i
m
e
[
j
]
=
=
0
i\ \%\ prime[j] == 0
i % prime[j]==0时,
i
i
i与
p
r
i
m
e
[
j
]
prime[j]
prime[j]不互质 用
f
(
i
∗
p
r
i
m
e
[
j
]
)
=
f
(
i
)
/
(
m
i
n
_
n
u
m
[
i
]
+
1
)
∗
(
m
i
n
_
n
u
m
[
i
]
+
2
)
f(i * prime[j]) = f(i) / (min\_num[i] + 1) * (min\_num[i] + 2)
f(i∗prime[j])=f(i)/(min_num[i]+1)∗(min_num[i]+2)
m
i
n
_
n
u
m
[
i
]
min\_num[i]
min_num[i]表示i的最小质因子的次数,在求因子个数同时求
m
i
n
_
n
u
m
[
i
]
min\_num[i]
min_num[i]
代码:
#include <cinttypes>
#include<iostream>
using namespace std;
#define MAX 1000000
int64_t num[MAX] = {0}; //因子的个数
int64_t prime[MAX] = {0}; //标记素数
int64_t min_num[MAX] = {0}; //最小因子的个数
int64_t triangle(int64_t n) {
return n * (n - 1) / 2;
}
//线性筛做
void init() {
for(int i = 2; i <= MAX; ++i) {
if(!prime[i]) {
prime[++prime[0]] = i;
num[i] = 2;
min_num[i] = 1;
}
for(int j = 1; j <= prime[0]; ++j) {
if(i * prime[j] > MAX) break;
prime[prime[j] * i] = 1;
if(i % prime[j]) {
num[prime[j] * i] = num[i] * num[prime[j]];
min_num[i * prime[j]] = 1;
} else {
num[i * prime[j]] = num[i] / (min_num[i] + 1) * (min_num[i] + 2);
min_num[i * prime[j]] = min_num[i] + 1;
break;
}
}
}
return ;
}
/*
//试除(和求最大因子原理一样)
int64_t f(int64_t x) {
int64_t n = 1;
for(int64_t i = 2; i * i <= x; ++i) {
if(x % i) continue;
int cnt = 0;
while(x % i == 0) {
cnt++;
x /= i;
}
n *= (cnt + 1);
}
if(x - 1) n *= 2;
return n;
}
*/
int main() {
/*
for(int i = 1; i <= MAX; ++i) {
num[i] = f(i);
}
*/
init();
int64_t n = 2;
while(1) {
if(n % 2 == 1) {
if (num[n] * num[(n - 1) >> 1] > 500) break;
} else {
if(num[n >> 1] * num[n - 1] > 500) break;
}
n++;
}
cout << triangle(n) << endl;
return 0;
}
Problem13大整数加法 文件中输入
Large sum
Work out the first ten digits of the sum of the following one-hundred 50-digit numbers.
计算出以下一百个50位数的和的前十位数字。
37107287533902102798797998220837590246510135740250
46376937677490009712648124896970078050417018260538
74324986199524741059474233309513058123726617309629
91942213363574161572522430563301811072406154908250
23067588207539346171171980310421047513778063246676
89261670696623633820136378418383684178734361726757
28112879812849979408065481931592621691275889832738
44274228917432520321923589422876796487670272189318
47451445736001306439091167216856844588711603153276
…
大整数
#include<stdio.h>
#define max 55
#include <string.h>
char str[max + 5] = {0};
int ans[max + 5] = {0};
//ans[0] 记录位数
int main() {
while(~scanf("%s", str)) {
int len = strlen(str);
if(len > ans[0]) ans[0] = len;
for(int i = 0; i < len; ++i) {
ans[len - i] += str[i] - '0';
}
//处理进位,等
for(int i = 1; i <= ans[0]; ++i) {
if(ans[i] < 10) continue;
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
ans[0] += (i == ans[0]);
}
}
for(int i = ans[0]; i > ans[0] - 10; i--) {
printf("%d", ans[i]);
}
printf("\n");
return 0;
}
扩展:求 a b a^b ab
HZOJ 54超级大整数
Problem14考拉兹序列 记忆化
记忆化
#include <stdio.h>
#define max 1000000
#define size 1000000
int keep[max + 5] = {0};
typedef long long ll;
ll getlen(ll n) {
if(n == 1) return 1;
if(n <= max && keep[n]) return keep[n];
ll ret = 0;
if(!(n & 1)) ret = getlen(n >> 1) + 1;
else ret = getlen(3 * n + 1) + 1;
if(n <= size) keep[n] = ret;
return ret;
}
int main() {
ll ans = 0, len = 0;
for(int i = 1; i < max; ++i) {
ll temp = getlen(i);
if(temp > len) {
ans = i;
len = temp;
}
}
printf("%lld %lld\n", ans, len);
return 0;
}
Problem15排列组合公式
#include <inttypes.h>
#include <stdio.h>
int main() {
int64_t ans = 1, m = 20;
for(int64_t i = 40; i >= 21; i--) {
ans *= i;
while(m && ans % m == 0) {
ans /= m;
m--;
}
}
printf("%" PRId64 "\n", ans);
return 0;
}
Problem16数字的幂 大整数
#include <iostream>
using namespace std;
#define MAX 1000000
long long ans[MAX + 5] = {1, 1};
int main() {
int n = 100;
long long sum = 0;
while(n--) {
for(long long i = 1; i <= ans[0]; ++i) {
ans[i] *= 1024;
}
for(long long i = 1; i <= ans[0]; ++i) {
if(ans[i] < 10) continue;
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
ans[0] += (i == ans[0]);
}
}
for(long long i = ans[0]; i >= 1; --i) {
sum += ans[i];
}
cout << sum << endl;
return 0;
}
Problem17英文字母个数
#include<stdio.h>
int get_letters(int n) {
static int arr1[20] = {
0, 3, 3, 5, 4, 4, 3, 5, 5, 4, 3,
6, 6, 8, 8, 7, 7, 9, 8, 8,
};
static int arr2[10] = {
0, 0, 6, 6, 5, 5, 5, 7, 6, 6
};
if(n < 20) {
return arr1[n];
} else if(n < 100) {
return arr2[n / 10] + arr1[n % 10];
} else if(n < 1000) {
if(n % 100 == 0) {
return arr1[n / 100] + 7;
}
return arr1[n / 100] + 10 + get_letters(n % 100);
} else {
return 11;
}
}
int main() {
int sum = 0;
for(int i = 1; i <= 1000; ++i) {
sum += get_letters(i);
}
printf("%d\n", sum);
return 0;
}
Problem18记忆化 动态规划 dfs
加记忆化
#include<iostream>
using namespace std;
#define MAX 20
int val[MAX][MAX];
int keep[MAX][MAX] = {0}; //记忆化
int dfs(int i, int j, int n) {
if(i + 1 == n) return val[i][j];
if(keep[i][j]) return keep[i][j];
int val1 = dfs(i + 1, j, n);
int val2 = dfs(i + 1, j + 1, n);
return keep[i][j] = (val1 > val2 ? val1 : val2) + val[i][j];
}
int main() {
for(int i = 0; i < MAX; i++) {
for(int j = 0; j <= i; j++) {
cin >> val[i][j];
}
}
cout << dfs(0, 0, MAX) << endl;
return 0;
}
动态规划
#include<iostream>
#include <algorithm>
using namespace std;
#define max 20
int val[max][max] = {0};
int main() {
for(int i = 0; i < max; ++i) {
for(int j = 0; j < i; ++j) {
cin >> val[i][j];
}
}
for(int i = max - 2; i >= 0; i--) {
for(int j = 0; j <= i; ++j) {
val[i][j] += ( val[i + 1][j] > val[i + 1][j + 1] ? val[i + 1][j] : val[i + 1][j + 1]);
}
}
cout << val[0][0] << endl;
return 0;
}
Problem20阶乘数字和 大整数
#include<stdio.h>
#define MAX 500
int ans[MAX] = {1, 1, 0};
int main() {
int sum = 0;
for(int i = 1; i <= 100; ++i) {
for(int j = 1; j <= ans[0]; ++j) {
ans[j] *= i;
}
for(int k = 1; k <= ans[0]; ++k) {
if(ans[k] < 10) continue;
ans[k + 1] += ans[k] / 10;
ans[k] %= 10;
ans[0] += (k == ans[0]);
}
}
for(int i = 1; i <= ans[0]; ++i) {
sum += ans[i];
}
printf("%d\n", sum);
return 0;
}
Problem21因子和
Problem24 字典序全排列
#include<iostream>
using namespace std;
#define STATUS 1000000
#define MAX_N 15
int fac[MAX_N] = {0}; //存10个数的阶乘;
int num[MAX_N] = {0}; //存10个数是否用过
//求阶乘
void init() {
fac[0] = 1;
num[0] = 1;
for(int i = 1; i <= 10; ++i) {
fac[i] = i * fac[i - 1];
num[i] = 1;
}
return ;
}
//返回两个数,一个是当前数x从零跳到几在主函数中输出
//另一个返回还需跳几个状态n
int get_num(int n, int fac, int &x) {
int step = n / fac;
int j = 0;
//让x从第一个没用过的数开始跳
while(!num[j]) j++;
x = j;
while(step) {
if(num[x + 1]) {
x++;
step--;
} else {
x++;
}
}
num[x] = 0;
n %= fac;
return n;
}
int main() {
init();
int n = 250 - 1; //当前需要跳得次数
for(int i = 10; i >= 1; i--) {
int x;
n = get_num(n, fac[i - 1], x);
cout << x;
}
cout << endl;
return 0;
}
字典序全排列函数
#include <algorithm>
#include<iostream>
using namespace std;
int main() {
int num[10];
for(int i = 0; i < 10; ++i) num[i] = i;
int k = 1000000 - 1;
do {
next_permutation(num, num + 10);
k--;
} while(k);
for(int i = 0; i < 10; ++i) {
cout << num[i];
}
cout << endl;
return 0;
}
Problem25大整数斐波那契 滚动数组
#include<stdio.h>
#include <math.h>
int f[3][1005];
int main() {
int n = 2;
//f[1] = 1, f[2] = 1;
f[1][0] = 1;
f[1][1] = 1;
f[2][0] = 1;
f[2][1] = 1;
while(f[n % 3][0] < 1000){
n += 1;
int *a = f[n % 3], *b = f[(n - 1) % 3], *c = f[(n - 2) % 3];
for(int i = 1; i <= b[0]; ++i) {
a[i] = b[i] + c[i];
}
a[0] = b[0];
for(int i = 1; i <= a[0]; ++i) {
if(a[i] < 10) continue;
a[i + 1] += a[i] / 10;
a[i] %= 10;
a[0] += (i == a[0]);
}
//f[n % 3] = f[(n - 1) % 3] + f[(n - 2) % 3];
//printf("%d\n", f[n % 3]);
}
printf("%d\n", n);
return 0;
}
#include <stdio.h>
int main() {
int fib[3][1005] = {0};
fib[0][0] = fib[0][1] = 1;
fib[1][0] = fib[1][1] = 1;
int n = 1;
while(fib[n % 3][0] < 1000) {
n++;
for(int i = 1; i <= fib[(n - 1) % 3][0]; ++i) {
fib[n % 3][i] = fib[(n - 1) % 3][i] + fib[(n - 2) % 3][i];
}
fib[n % 3][0] = fib[(n - 1) % 3][0];
for(int i = 1; i <= fib[n % 3][0]; i++) {
if(fib[n % 3][i] < 10) continue;
fib[n % 3][i + 1] += fib[n % 3][i] / 10;
fib[n % 3][i] %= 10;
fib[n % 3][0] += (i == fib[n % 3][0]);
}
}
printf("%d\n", n + 1);
return 0;
}
Problem26求循环节
列竖式计算可以看到每次余数都乘10,当余数在之前出现过时就开始出现循环节
1/n 竖式计算时余数有n种(0 – n-1)其中余数是0时就除尽了,因此循环节最长为n-1;
#include <cstring>
#include <iostream>
using namespace std;
#define MAX 1000
int rest[MAX]; //记录最初出现的位置,若是0表示没出现
int get_maxlen(int n) {
memset(rest, 0, sizeof(rest));
int r = 10 % n, index = 1;
while(r && !rest[r]) {
rest[r] = index++; //index同时记录位置和长度
r *= 10;
r %= n;
}
return r ? index - rest[r] :0;
}
int main() {
int num, len, MAXLEN = 0;
for(int i = 2; i <= MAX; ++i) {
len = get_maxlen(i);
if(len > MAXLEN) num = i, MAXLEN = len;
}
cout << num << endl;
return 0;
}
Problem28规律 螺旋数阵对角线
思路: 找每圈的四个角的规律
#include<stdio.h>
int main() {
int sum = 1;
for(int l = 3; l <= 1001; l += 2) {
sum += 4 * l * l - 6 * l + 6;
}
printf("%d\n", sum);
return 0;
}
发现
s
u
m
=
4
∗
(
3
2
+
5
2
+
7
2
+
.
.
.
+
100
1
2
)
−
6
∗
(
3
+
5
+
7
+
.
.
)
+
C
sum = 4*(3^2 + 5^2 + 7^2 +...+ 1001^2) - 6*(3 + 5 + 7 +.. ) + C
sum=4∗(32+52+72+...+10012)−6∗(3+5+7+..)+C
可推倒出一个公式
Problem29 大整数去重
#include <algorithm>
#include <cstring>
#include<iostream>
#include <cstdlib>
using namespace std;
#define maxn 10000
#define maxm 210
int *result[maxn + 5];
int result_len = 0;
int find_result(int *num) {
for(int i = 0; i < result_len; ++i) {
if(memcmp(result[i], num, sizeof(int) * maxm) == 0) {
return i + 1;
}
}
return 0;
}
int *calc(int a, int b) {
int *temp = (int *)calloc(sizeof(int), maxm);
temp[0] = 1, temp[1] = 1;
for(int i = 0; i < b; ++i) {
for(int j = 1; j <= temp[0]; j++) temp[j] *= a;
for(int j = 1; j <= temp[0]; ++j) {
if(temp[j] < 10) continue;
temp[j + 1] += temp[j] / 10;
temp[j] %= 10;
temp[0] += (j == temp[0]);
}
}
return temp;
}
int main() {
for(int a = 2; a <= 100; ++a) {
for(int b = 2; b <= 100; ++b) {
int *temp = calc(a, b);
if(find_result(temp) == 0) {
result[result_len++] = temp;
}
}
}
cout << result_len << endl;
return 0;
}
Problem30各位数字的5次幂
#include <stdio.h>
#include <math.h>
#define maxn 3554294
int is_val(int n) {
int x = n, temp = 0;
while(x) {
temp += (int)pow(x % 10, 5);
x /= 10;
}
return temp == n;
}
int main() {
int sum = 0;
for(int i = 2; i <= maxn; ++i) {
if(is_val(i)) sum += i;
}
printf("%d\n", sum);
return 0;
}
Problem31硬币求和 递推★★★
最笨的递归写法
#include<iostream>
using namespace std;
int w[10] = {0, 1, 2, 5, 10, 20, 50, 100, 200};
int keep[10][210];
int f(int k, int n) {
if(n == 0) return 1;
if(n < 0) return 0;
if(k == 1) return 1;
return f(k - 1, n) + f(k, n - w[k]);
}
int main() {
cout << f(8, 200) << endl;
return 0;
}
Problem32求数字位数
- 求数字的位数 f l o o r ( l o g 10 ( n ) ) + 1 floor(log10(n)) + 1 floor(log10(n))+1, 求出 i , j , i ∗ j i, j, i * j i,j,i∗j 的位数和筛去不是九的
- 针对每组 i , j i, j i,j 依次把 i , j , i ∗ j i, j, i * j i,j,i∗j各个位标记有重复时不符合条件
#include<cmath>
#include<iostream>
#include <cstring>
using namespace std;
int num[15];
int digit(int i, int j) {
int sum_digit = 0;
sum_digit += (int)floor(log10(i)) + 1;
sum_digit += (int)floor(log10(j)) + 1;
sum_digit += (int)floor(log10(i * j)) + 1;
return sum_digit;
}
int is_val(int n) {
while(n) {
if(n % 10 == 0) return 0;
if(num[n % 10]) return 0;
num[n % 10] = 1;
n /= 10;
}
return 1;
}
int is_ok(int i, int j) {
memset(num, 0, sizeof(num));
if(is_val(i) && is_val(j) && is_val(i * j)) return 1;
return 0;
}
int main() {
int arr[100000] = {0};
long long sum = 0;
for(int i = 1; i <= 100; i++) {
for(int j = i + 1; ; ++j) {
int digs = digit(i, j);
if(digs < 9) continue;
else if(digs > 9) break;
if(is_ok(i, j) && !arr[i * j]) sum += i * j, arr[i * j] = 1;
}
}
cout << sum << endl;
return 0;
}
Problem33错误约分正确答案
判断两个分数是否相等转化为乘法
#include<stdio.h>
int gcd(int a, int b) {
return (b ? gcd(b, a % b) : a);
}
int is_val(int x, int y) {
int x1 = x / 10, x2 = x % 10;
int y1 = y / 10, y2 = y % 10;
if(!x2 || !y2) return 0;
if(x1 == y1 && x2 * y == x * y2) return 1;
if(x1 == y2 && x2 * y == x * y1) return 1;
if(x2 == y1 && x1 * y == x * y2) return 1;
if(x2 == y2 && x1 * y == x * y1) return 1;
}
int main() {
int x = 1, y = 1, c = 1;
for(int a = 10; a < 100; ++a) {
for(int b = a + 1; b < 100; b++) {
if((!is_val(a, b))) continue;
x *= a, y *= b;
c = gcd(x, y);
x /= c, y /= c;
}
}
printf("%d\n", y);
return 0;
}
Problem34各位数字的阶乘和 记忆化
#include <stdio.h>
#define maxn 2903040
int k[10] = {1,1};
int f(int n) {
if(k[n]) return k[n]; //记忆化
return k[n] = f(n - 1) * n;
}
int is_val(int n) {
int x = n, sum = 0;
while(x) {
sum += f(x % 10);
x /= 10;
}
return sum == n;
}
int main() {
int sum = 0;
for(int i = 3; i <= maxn; ++i) {
if(is_val(i)) sum += i;
}
printf("%d\n", sum);
return 0;
}
Problem35圆环素数 位数
#include <cmath>
#include<iostream>
using namespace std;
#define MAX 1000000
int prime[MAX], is_prime[MAX];
void init() {
for(int i = 2; i <= MAX; ++i) {
if(!is_prime[i]) prime[++prime[0]] = i;
for(int j = 1; j <= prime[0]; ++j) {
if(i * prime[j] > MAX) break;
is_prime[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return ;
}
int is_val(int n) {
if(is_prime[n]) return 0;
int digit = floor(log10(n)) + 1;
int temp = pow(10, digit - 1);
for(int i = 1; i < digit; ++i) {
int t;
t = n % temp;
t = t * 10 + n / temp;
if(is_prime[t]) return 0;
n = t;
}
return 1;
}
int main() {
init();
int sum = 0;
for(int i = 2; i <= MAX; ++i) {
if(is_val(i)) sum++;
}
cout << sum << endl;
return 0;
}
Problem36双进制下回文数
#include <stdio.h>
#define maxn 1000000
//判断base进制下是不是回文数
int is_val(int n, int base) {
int m = n, sum = 0;
while(m) {
sum = sum * base + m % base;
m /= base;
}
return sum == n;
}
int main() {
int sum = 0;
for(int i = 1; i <= maxn; ++i) {
if(is_val(i, 10) && is_val(i, 2)) sum += i;
}
printf("%d\n", sum);
return 0;
}
Problem37左截右截依旧是素数
#include <math.h>
#include<stdio.h>
#define MAX 1000000
int prime[MAX] = {0};
int is_prime[MAX] = {1, 1, 0};
void init() {
for(int i = 2; i <= MAX; ++i) {
if(!is_prime[i]) prime[++prime[0]] = i;
for(int j = 1; j <= prime[0]; ++j) {
if(prime[j] * i > MAX) break;
is_prime[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
return ;
}
int is_val(int n) {
int digit = floor(log10(n)) + 1; //存位数
int h = pow(10, digit - 1);
int tmp = n;
/*
//从右截
for (int i = 0;i < digit - 1 ;i++) {
tmp /= 10;
if(is_prime[tmp]) return 0;
}
//从左截
tmp = n;
for (int i = 0;i < digit - 1;i++) {
tmp %= h;
if(is_prime[tmp]) return 0;
h /= 10;
}
*/
//从右往左截
while(tmp / 10) {
tmp /= 10;
if(is_prime[tmp]) return 0;
}
//从左截
int temp2 = n;
while(temp2 > 10) {
int digit1 = floor(log10(temp2)) + 1;
int h = pow(10, digit1 - 1);
if(is_prime[temp2 % h]) return 0;
temp2 %= h;
}
return 1;
}
int main() {
init();
int sum = 0;
int flag = 11;
for(int i = 1; i <= prime[0] && flag; ++i) {
if(prime[i] < 10) continue;
if(is_val(prime[i])) {
sum += prime[i];
flag--;
}
}
printf("%d\n", sum);
return 0;
}
Problem38连接几个乘积为 全数字判断
#include<stdio.h>
#include<math.h>
int digits(long long n) {
if(!n) return 1;
return floor(log10(n)) + 1;
}
long long calc(int x) {
long long n = 1, ans = 0;
while(digits(ans) < 9) {
ans *= pow(10, digits(x * n));
ans += n * x;
n += 1;
}
if(digits(ans) - 9) return 0;
int num[10] = {1, 0}; //标记数组
int temp = ans;
while(temp) {
if(num[temp % 10]) return 0;
num[temp % 10] += 1;
temp /= 10;
}
return ans;
}
int main() {
long long ans = 0;
for(int i = 1; i < 10000; ++i) {
int tmp = calc(i);
if(tmp > ans) ans = tmp;
}
printf("%lld\n", ans);
return 0;
}
Problem39素勾股数的使用
找出素勾股数再求出他们的倍数
#include<iostream>
using namespace std;
#define maxn 1000
int cnt[maxn + 5] = {0};
int gcd(int a, int b) {
return (b ? gcd(b, a % b) : a);
}
int main() {
for(int n = 1; n <= 32; ++n) {
for(int m = n + 1; m <= 32; m++) {
if(gcd(m, n) - 1) continue;
int a = m * m - n * n;
int b = 2 * m * n;
int c = m * m + n * n;
for(int p = a + b + c; p <= 1000; p += (a + b + c)) {
cnt[p] += 1;
}
}
}
int ans = 0;
for(int i = 1; i <= maxn; i++){
if(cnt[i] > cnt[ans]) ans = i;
}
cout << ans << endl;
return 0;
}
Problem44二分查找 五边形数
-
判断是不是五边形数用二分法,二分函数而不是二分数组
-
确定边界
思路:
p
(
j
)
p(j)
p(j)表示五边形数,D表示差
-
从2往后枚举 j j j从第n项往前找五边形数再看他们与 p ( j ) p(j) p(j) 的和与差是不是都是五边形数,当 p ( j ) p(j) p(j)与 p ( j − 1 ) p(j - 1) p(j−1)的差都大于D就不用再找了因为差只会越来越大
-
设个变量 k k k,从 j − 1 j - 1 j−1开始一点点减小只要 p ( j ) − p ( k ) < D p(j) - p(k) < D p(j)−p(k)<D 就有可能找到更小的差,循环内再判断 p ( j ) + p ( k ) p(j) + p(k) p(j)+p(k) 与 p ( j ) − p ( k ) p(j) - p(k) p(j)−p(k) 是不是五边形数
因为是递增函数所以用二分来做,比如判断15是不是五边形数那么就在 1 − p ( 15 ) 1 - p(15) 1−p(15)区间内用二分来找
#include <cinttypes>
#include<iostream>
using namespace std;
inline int64_t Pentagonal(int64_t n) {
return n * (3 * n - 1) / 2;
}
int is_pentagonal(int64_t p) {
int head = 1, tail = p;
int mid;
while(head <= tail) {
mid = (head + tail) >> 1;
if(Pentagonal(mid) == p) return 1;
else if(Pentagonal(mid) > p) {
tail = mid - 1;
} else {
head = mid + 1;
}
}
return 0;
}
int main() {
int64_t j = 2, D = INT64_MAX;
while(Pentagonal(j) - Pentagonal(j - 1) < D) {
int64_t k = j - 1;
while(Pentagonal(j) - Pentagonal(k) < D) {
if(is_pentagonal(Pentagonal(j) + Pentagonal(k)) && is_pentagonal(Pentagonal(j) - Pentagonal(k))) {
D = Pentagonal(j) - Pentagonal(k);
cout << k << " " << j << " " << D << endl;
}
k--;
if(!k) break;
}
j++;
}
cout << D << endl;
return 0;
}
#include<stdio.h>
long long Pentagonal(long long n) {
return n * (3 * n - 1) / 2;
}
long long binary_search(long long (*func)(long long), long long n, long long x) {
long long head = 1, tail = n, mid;
while(head <= tail) {
mid = (head + tail) >> 1;
if(func(mid) == x) return mid;
if(func(mid) < x) head = mid + 1;
else tail = mid - 1;
}
return 0;
}
int main() {
long long n = 2, D = INT32_MAX, pk, pj;
while(Pentagonal(n) - Pentagonal(n - 1) < D) {
pk = Pentagonal(n);
for(long long j = n - 1; j >= 1; j--) {
pj = Pentagonal(j);
if(pk - pj >= D) break;
long long ind1, ind2;
ind1 = binary_search(Pentagonal, 2 * n, pk + pj);
if(ind1) {
ind2 = binary_search(Pentagonal, n, pk - pj);
if(ind2) D = pk - pj;
}
}
n++;
}
printf("%lld\n", D);
return 0;
}
Problem45六边形数中 二分查找 五边形数
六边形中找五边形
#include<stdio.h>
typedef long long int1;
int1 Triangle(int1 n) {
return n * (n + 1) / 2;
}
int1 Pentangle(int1 n) {
return n * (3 * n - 1) / 2;
}
int1 Hexangonal(int1 n) {
return n * (2 * n - 1);
}
int1 binary_search(int1 (*num)(int1), int1 n, int1 x) {
int1 head = 0, tail = n - 1, mid;
while(head <= tail) {
mid = (head + tail) >> 1;
if(num(mid) == x) return mid;
if(num(mid) < x) head = mid + 1;
else tail = mid - 1;
}
return -1;
}
int main() {
int1 n = 144;
while(binary_search(Pentangle, 2 * n, Hexangonal(n)) == -1) n += 1;
printf("%d\n", Hexangonal(n));
return 0;
}
Problem46不一定奇合数=素数+平方2倍
#include<iostream>
using namespace std;
#define maxn 1000000
int prime[maxn + 5] = {0};
int is_prime[maxn + 5] = {0};
int is_sqrt(int n) {
return 2 * n * n;
}
void init() {
for(int i = 2; i <= maxn; ++i) {
if(!is_prime[i]) prime[++prime[0]] = i;
for(int j = 1; j <= prime[0]; ++j) {
if(i * prime[j] > maxn) break;
is_prime[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return ;
}
bool binary_search(int(*func)(int), int l, int r, int x) {
if(l > r) return false;
int mid = (l + r) >> 1;
if(func(mid) == x) return true;
if(func(mid) < x) l = mid + 1;
else r = mid - 1;
return binary_search(func, l, r, x);
}
bool check(int n) {
for(int j = 1; j <= prime[0] && prime[j] < n; j++) {
if(binary_search(is_sqrt, 1, n - prime[j], n - prime[j])) return true;
}
return false;
}
int main() {
init();
int ans = 0;
for(int i = 9; i <= maxn; i += 2) {
if(!is_prime[i]) continue;
if(check(i)) continue;
ans = i;
break;
}
cout << ans << endl;
return 0;
}
Problem47素因子种类
用线性筛,筛素数的同时用一个数组存素因子种类个数,素数就一个素因子
#include<iostream>
using namespace std;
#define MAX 1000000
int prime[MAX], cnt[MAX];
void init() {
for(int i = 2; i <= MAX; ++i) {
if(!prime[i]) {
prime[++prime[0]] = i;
cnt[i] = 1;
}
for(int j = 1; j <= prime[0]; ++j) {
if(i * prime[j] > MAX) break;
prime[i * prime[j]] = 1;
if(i % prime[j] == 0) {
cnt[i * prime[j]] = cnt[i];
} else {
cnt[i * prime[j]] = cnt[i] + 1;
}
}
}
return ;
}
int main() {
init();
for(int i = 2; ;i++) {
if(cnt[i] != 4) continue;
if(cnt[i] != cnt[i + 1]) continue;
if(cnt[i + 1] != cnt[i + 2]) continue;
if(cnt[i + 2] != cnt[i + 3]) continue;
cout << i << endl;
break;
}
return 0;
}