欧拉计划提升编码能力

欧拉计划

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[(i1)%3]+F[(i2)%3]F[i]=F[i1]+F[i2]

#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)ab

#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)(a2ab+b2)
  • 立方差: a 3 − b 3 = ( a − b ) ( a 2 + a b + b 2 ) a^3 - b^3 = (a-b)(a^2+ab+b^2) a3b3=(ab)(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, 有四个性质

  1. (na, nb, nc)也是勾股数
  2. (a, b, c)之间两两互质
  3. a, b必为一奇一偶
  4. 任何素勾股数均可有如下表示(其中 n < m, gcd(n, m) = 1)​​​​​​​​​​​​​​​​​​​​​
    ​​​​​​​​​​​​​​​​​​​​​​​​​ a = 2 ∗ m ∗ n a = 2 * m * n a=2mn
    b = m 2 − n 2 b = m^2 - n^2 b=m2n2
    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因子个数

在这里插入图片描述
关键点

  1. 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} p2a2p3a3...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)
  2. 若A,B互质,则 f ( A ∗ B ) = f ( A ) ∗ f ( B ) f(A*B) = f(A) * f(B) f(AB)=f(A)f(B)

本题两种做法:试除法 和 线性筛来做
试除法在欧拉3求最大素因子时用到,从2开始除尽所有素因子

用线性筛最关键的一点是什么?
主要是第二点
线性筛原理是用一个数乘以素数来筛合数, i ∗ p r i m e [ j ] i * prime[j] iprime[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(iprime[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(iprime[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(iprime[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求数字位数

在这里插入图片描述

  1. 求数字的位数 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,ij 的位数和筛去不是九的
  2. 针对每组 i , j i, j i,j 依次把 i , j , i ∗ j i, j, i * j i,j,ij各个位标记有重复时不符合条件
#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二分查找 五边形数

在这里插入图片描述

  1. 判断是不是五边形数用二分法,二分函数而不是二分数组

  2. 确定边界

思路
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(j1)的差都大于D就不用再找了因为差只会越来越大

  • 设个变量 k k k,从 j − 1 j - 1 j1开始一点点减小只要 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) 1p(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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值