机试day8

最大公约数

最大公约数_牛客题霸_牛客网 (nowcoder.com)

描述

输入两个正整数,求其最大公约数。

输入描述

测试数据有多组,每组输入两个正整数。

输出描述

对于每组输入,请输出其最大公约数。

思路:辗转相除法,也可以写个递归的取模函数

#include <iostream>
using namespace std;

int main() {
    int a, b;
    while (cin >> a >> b) {
        int tmp;
        while (b) {
            tmp = a % b;
            a = b;
            b = tmp;
        }
        cout << a << endl;
    }
}

最简真分数

最简真分数_牛客题霸_牛客网 (nowcoder.com)

描述

给出n个正整数,任取两个数分别作为分子和分母组成最简真分数,编程求共有几个这样的组合。

输入描述

每组包含n(n<=600)和n个不同的整数,整数大于1且小于等于1000。

输出描述

每行输出最简真分数组合的个数。

思路:转换为求最大公约数,如果不为1,则为最简真分数

#include <iostream>
using namespace std;

int is_zuijian(int a, int b) {
    int tmp;
    while (b) {
        tmp = a % b;
        a = b;
        b = tmp;
    }
    if (a != 1)
        return 0;
    else
        return 1;
}

int main() {
    int n;
    while (cin >> n) {
        if (n == 0)
            break;
        int num[n];
        for (int i = 0; i < n; i++) {
            cin >> num[i];
        }
        int m = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                m += is_zuijian(num[i], num[j]);
            }
        }
        cout << m << endl;
    }
}

素数判定

素数判定_牛客题霸_牛客网 (nowcoder.com)

描述

给定一个数n,要求判断其是否为素数(0,1,负数都是非素数)。

输入描述

测试数据有多组,每组输入一个数n。

输出描述

对于每组输入,若是素数则输出yes,否则输入no。

思路:对于大于1的数遍历取模,若余数为0,说明存在银子,不是质数【巧妙方法:比到根号n即可,数论知识】

#include <iostream>
#include <math.h>
using namespace std;

int is_prime(int n) {
    int prime = 1;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            prime = 0;
            break;
        }
    }
    return prime;
}

int main() {
    int n, prime;
    while (cin >> n) {
        if (n <= 1)
            prime = 0;
        else {
            prime = is_prime(n);
        }
        if (prime)
            cout << "yes" << endl;
        else
            cout << "no" << endl;
    }
}

素数

素数_牛客题霸_牛客网 (nowcoder.com)

描述

输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。

输入描述

输入有多组数据。 每组一行,输入n。

输出描述

输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数(素数之间用空格隔开,最后一个素数后面没有空格),如果没有则输出-1。

思路:本题无非就是多了个要求素数个位为1,直接for循环从11每次加10,每个循环内做一次上一题的is_prime判断

#include <iostream>
#include <math.h>
using namespace std;

int is_prime(int n) {
    int prime = 1;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            prime = 0;
            break;
        }
    }
    return prime;
}

int main() {
    int n, has_prime;
    while (cin >> n) {
        has_prime = 0;
        for (int i = 11; i < n; i += 10) {
            if (is_prime(i)) {
                has_prime++;
                cout << i << ' ';
            }
        }
        if (!has_prime)
            cout << "-1";
        cout << endl;
    }
}

瞅了眼题解,发现个搞笑的:他的做法和id很搭配

image-20230610171646980.png

Prime Number

Prime Number_牛客题霸_牛客网 (nowcoder.com)

描述

Output the k-th prime number.

输入描述

k≤10000

输出描述

The k-th prime number.

思路:本题依然很简单,为了应对不同可能的k值,我们可以提前先找好10000个素数存入vector,直接下标k取值就好

做了一个加速【感觉不明显】,首先已经存好2,剩下的只检查奇数

#include <iostream>
#include <vector>
#include <math.h>
using namespace std;

int is_prime(int n) {
    int prime = 1;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            prime = 0;
            break;
        }
    }
    return prime;
}

int main() {
    vector<int> prime_number;
    prime_number.push_back(2);
    int i = 3;
    while (prime_number.size() <= 10000) {
        if (is_prime(i))
            prime_number.push_back(i);
        i += 2;
    }
    int k;
    while (cin >> k) {
        cout << prime_number[k-1] << endl;
    }
}

额外学习下:埃氏筛法

埃氏筛法:用已经筛选出的素数去过滤所有能整除它的数,但是只能在给定范围内快速筛选,所需时间少很多

const int maxn = 10000000;
int prime[maxn];
bool IsPrime[maxn];//0代表都是素数
void Find_Prime(){
    IsPrime[1] = 1;//1不是素数
    int pNum = 0;
    for(int i = 2; i <= maxn; i++){
        if(!IsPrime[i]){
            prime[pNum++] = i;  
        }
        for(int j = 0; j < pNum && i * prime[j] <= maxn; j++){
            IsPrime[i * prime[j]] = 1;
            if(i % prime[j] == 0){
                break;
            }
        }
    }
}

质因数的个数

质因数的个数_牛客题霸_牛客网 (nowcoder.com)

描述

求正整数N(N>1)的质因数的个数。 相同的质因数需要重复计算。如120=22235,共有5个质因数。

输入描述

可能有多组测试数据,每组测试数据的输入是一个正整数N,(1<N<10^9)。

输出描述

对于每组数据,输出N的质因数的个数。

思路:完全没必要用书上的先求一定范围素数,在一个个遍历

可以从2开始,一个个去除N,每遇到一个余0的num++,直到N完全没有该因子

#include <iostream>
using namespace std;

int main() {
    int N;
    while (cin >> N) {
        int num = 0;
        int factor = 2;
        while (N != 1) {
            while (N % factor == 0) {
                N /= factor;
                num++;
            }
            factor++;
        }
        cout << num << endl;
    }
}

约数的个数

描述

输入n个整数,依次输出每个数的约数的个数

输入描述

输入的第一行为N,即数组的个数(N<=1000) 接下来的1行包括N个整数,其中每个数的范围为(1<=Num<=1000000000)

输出描述

可能有多组输入数据,对于每组输入数据, 输出N行,其中每一行对应上面的一个数的约数的个数。

法1:偏暴力搜索

思路:由欧拉函数知,一个合数的因数数目为所有素因数数目+1的乘积,例如$6=2\times3$,即6有$(1+1)\times(1+1)=4$个因数,$12=2^2\times3$,即12有$(2+1)\times(1+1)=6$个因数,以此方式计算约束个数

但是由于需要找出素因数,会耗费不少时间(假如N很大,factor++这里循环较长时间),导致程序超时【当然有解决办法,即提前算好素因数】

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main() {
    int N;
    while (cin >> N) {
        int num[N];
        for (int i = 0; i < N; i++) {
            cin >> num[i];
            int factor = 2;
            vector<int> M;
            while (num[i] != 1) {
                int factor_num = 0;
                while (num[i] % factor == 0) {
                    num[i] /= factor;
                    factor_num++;
                }
                if (factor_num)
                    M.push_back(factor_num + 1);
                factor++;
            }
            int result = 1;
            for (int i = 0; i < M.size(); i++) {
                result *= M[i];
            }
            cout << result << endl;
        }
    }
}

法2:因数成对出现

除了完全平方数外,都是成对出现,因此只需计算到$\sqrt{n}$即可,每次+2,如果完全平方数中间+1

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main() {
    int N;
    while (cin >> N) {
        int num[N];
        for (int i = 0; i < N; i++) {
            cin >> num[i];
            int result = 0, j;
            for (j = 1; j < sqrt(num[i]); j++) {
                if (num[i] % j == 0) {
                    result += 2;
                }  
            }
            if (num[i] == j * j)
                result++;
            cout << result << endl;
        }
    }
}

整除问题

整除问题_牛客题霸_牛客网 (nowcoder.com)

描述

给定n,a求最大的k,使n!可以被a^k整除但不能被a^(k+1)整除。

输入描述

两个整数n(2<=n<=1000),a(2<=a<=1000)

输出描述

一个整数.

思路:首先本题目肯定无法使用一般数字类型,因为n的阶乘太大;因此需要结合因数拆解来做

这里有一些学网安数当时的味,发现很多当时学的完全可以用到现在这里

这道题根本上还是得去做因式分解,文字不好描述,直接上案例:设a=12,则$12=2^2\times3$,我们要做的是找到n的阶乘结果中包含了多少个2和3因数,假设含有i个2和j个3,那么k应该是i/2和j中最大的结果

首先还是获取1000以内的素数,使用模板!!!记住它

const int MAXN = 1001;
vector<int> prime;
bool isPrime[MAXN];

void getPrime() {
    memset(isPrime, true, sizeof(isPrime));
    isPrime[0] = false;
    isPrime[1] = false;
    for (int i = 2; i < MAXN; i++) {
        if (!isPrime[i]) {
            continue;
        } else {
            prime.push_back(i);
            for (int j = i * i; j < MAXN; j += i) {
                isPrime[j] = false; 
            }
        }
    }
}

getBiggestPrimeFactor用来获取a的最大素因数及其指数,getFactorNum获取n的阶乘中含有最大素因数个数

pair<int, int> getBiggestPrimeFactor(int a) {
    int upper_bound = sqrt(a) + 1;
    pair<int, int> biggestPrimeFactor = make_pair(1, 0);
    for (int i = 0; i < a && i < upper_bound; i++) {
        int factor = prime[i];
        int index = 0;
        while (a % factor == 0) {
            a /= factor;
            index++;
        }
        if (a == 1) {
            biggestPrimeFactor.first = factor;
            biggestPrimeFactor.second = index;
            break;
        }
    }
    if (a > 1) {
        biggestPrimeFactor.first = a;
        biggestPrimeFactor.second = 1;
    }
    return biggestPrimeFactor;
}

int getFactorNum(int n, int factor) {
    return n == 0? 0: n / factor + getFactorNum(n / factor, factor);
}

int main() {
    getPrime();
    int a, n;
    while (cin >> n >> a) {
        pair<int ,int> biggestPrimeFactor = getBiggestPrimeFactor(a);
        int factor = biggestPrimeFactor.first;
        int res = getFactorNum(n, factor);
        res /= biggestPrimeFactor.second;
        cout << res << endl;
    }
    return 0;
}

求root(N, k)

求root(N, k)_牛客题霸_牛客网 (nowcoder.com)

描述

N<k时,root(N,k) = N,否则,root(N,k) = root(N',k)。N'为N的k进制表示的各位数字之和。输入x,y,k,输出root(x^y,k)的值 (这里^为乘方,不是异或),2=<k<=16,0<x,y<2000000000,有一半的测试点里 x^y 会溢出int的范围(>=2000000000)

输入描述

每组测试数据包括一行,x(0<x<2000000000), y(0<y<2000000000), k(2<=k<=16)

输出描述

输入可能有多组数据,对于每一组数据,root(x^y, k)的值

思路:本题很难,需要做一定的数论推导,得到$N'=N\%(k-1)$的结论

同时要注意的是int类型可能无法装得下x、y,所以得用long long;同时需要用快速幂加速计算

img

#include <iostream>
using namespace std;

long long FastExponentiation(long long x, long long y, int k) {
    long long result = 1;
    while (y) {
        if (y % 2 == 1) {
            result *= x;
            result %= k;
        }
        y /= 2;
        x *= x;
        x %= k;
    }
    return result? result: k;
}

int main() {
    long long x, y;
    int k;
    while (cin >> x >> y >> k) {
        cout << FastExponentiation(x, y, k-1) << endl;
    }
}

计算两个矩阵的乘积

计算两个矩阵的乘积_牛客题霸_牛客网 (nowcoder.com)

描述

计算两个矩阵的乘积,第一个是23,第二个是32

输入描述

输入为两个矩阵,其中一个为23的矩阵,另一个为32的矩阵

输出描述

一个2*2的矩阵(每一个数字后都跟一个空格)

思路:就直接写

#include <iostream>
using namespace std;

int main() {
    int m1[2][3], m2[3][3];
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            cin >> m1[i][j];
        }
    }
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 2; j++) {
            cin >> m2[i][j];
        }
    }
    for (int i = 0; i < 2; i++) {  
        for (int j = 0; j < 2; j++) {
            int n = 0;
            for (int k = 0; k < 3; k++) {
                n += m1[i][k] * m2[k][j];
            }
            cout << n << " ";
        }
        cout << endl;
    }
}

矩阵幂

矩阵幂_牛客题霸_牛客网 (nowcoder.com)

描述

给定一个n*n的矩阵,求该矩阵的k次幂,即P^k。

输入描述

第一行:两个整数n(2<=n<=10)、k(1<=k<=5),两个数字之间用一个空格隔开,含义如上所示。 接下来有n行,每行n个正整数,其中,第i行第j个整数表示矩阵中第i行第j列的矩阵元素Pij且(0<=Pij<=10)。另外,数据保证最后结果不会超过10^8。

输出描述

对于每组测试数据,输出其结果。格式为: n行n列个整数,每行数之间用空格隔开,注意,每行最后一个数后面不应该有多余的空格。

思路:与上一题不同,这次矩阵大小会变化,因此定义个struct Matrix和矩阵乘法函数

同时,为了减少计算时间,使用了矩阵快速幂,与普通快速幂区别在于,初始值不是1,而是单位矩阵(对角线为1,其他为0)

#include <iostream>
using namespace std;

struct Matrix {
    int m[10][10];
};

Matrix Multiply(Matrix a, Matrix b, int n) {
    Matrix r;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            r.m[i][j] = 0;
        }
    }
    for (int i = 0; i < n; i++) {  
        for (int j = 0; j < n; j++) {
            for (int k = 0; k < n; k++) {
                r.m[i][j] += a.m[i][k] * b.m[k][j];
            }
        }
    }
    return r;
}

Matrix FastExponentiation(Matrix x, int n, int k) {
    Matrix r;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            r.m[i][j] = 0;
        }
        r.m[i][i] = 1;
    }
    while (k) {
        if (k % 2 == 1) {
            r = Multiply(r, x, n);
        }
        k /= 2;
        x = Multiply(x, x, n);
    }
    return r;
}

int main() {
    int n, k;
    while (cin >> n >> k) {
        Matrix p;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> p.m[i][j];
            }
        }
        p = FastExponentiation(p, n, k);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cout << p.m[i][j];
                if (j != n -1)
                    cout << " ";
            }
            cout << endl;
        }
    }
}

A+B for Matrices

A+B for Matrices_牛客题霸_牛客网 (nowcoder.com)

描述

This time, you are supposed to find A+B where A and B are two matrices, and then count the number of zero rows and columns.

输入描述

The input consists of several test cases, each starts with a pair of positive integers M and N (≤10) which are the number of rows and columns of the matrices, respectively. Then 2*M lines follow, each contains N integers in [-100, 100], separated by a space. The first M lines correspond to the elements of A and the second M lines to that of B. The input is terminated by a zero M and that case must NOT be processed.

输出描述

For each test case you should output in one line the total number of zero rows and columns of A+B.

思路:哎,就是一点点算;可以改进的点是读B的时候可先把行的0找出来

#include <iostream>
using namespace std;

int main() {
    int M, N;
    while (cin >> M >> N) {
        int A[M][N], B[M][N];
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                cin >> A[i][j];
            }
        }
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                cin >> B[i][j];
            }
        }
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                A[i][j] += B[i][j];
            }
        }
        int cnt = 0, flag = 0;
        for (int i = 0; i < M; i++) {
            flag = 1;
            for (int j = 0; j < N; j++) {
                if (A[i][j] != 0) {
                    flag = 0;
                }
            }
            cnt += flag;
        }
        for (int i = 0; i < N; i++) {
            flag = 1;
            for (int j = 0; j < M; j++) {
                if (A[j][i] != 0) {
                    flag = 0;
                    break;
                }
            }
            cnt += flag;
        }
        cout << cnt << endl;
    }
}

递推数列

描述

给定a0,a1,以及an=pa(n-1) + qa(n-2)中的p,q。这里n >= 2。 求第k个数对10000的模。

输入描述

输入包括5个整数:a0、a1、p、q、k。

输出描述

第k个数a(k)对10000的模。

思路:乍一看,不就是递归么,刷刷写好了,测试案例也没问题,一提交,报错!超时!!!

想想也是,清华上机题能简单么

#include <iostream>
using namespace std;

int get_a(int a0, int a1, int p, int q, int k) {
    if (k > 3)
        return (p * get_a(a0, a1, p, q, k-1) + q * get_a(a0, a1, p, q, k-2)) % 10000;
    else if (k == 3)
        return (p * get_a(a0, a1, p, q, k-1) + q * a1) % 10000;
    else if (k == 2)
        return (p * a1 + q * a0) % 10000;
    else if (k == 1)
        return a1 % 10000;
    else
        return a0 % 10000;
}

int main() {
    int a0, a1, p, q, k;
    while (cin >> a0 >> a1 >> p >> q >> k) {
        cout << get_a(a0, a1, p, q, k) << endl;
    }
}

法1:while型递归

思路:这样子写递归貌似会快

#include <iostream>
using namespace std;

int get_a(int a0, int a1, int p, int q, int k) {
    int cnt = 2, a2;
    p %= 10000; 
    q %= 10000;
    if (k == 0)
        return a0 % 10000;
    else if (k == 1)
        return a1 % 10000;
    else {
        while (cnt <= k) {
            a2 = (p * a1 + q * a0) % 10000;
            a0 = a1;
            a1 = a2;
            cnt++;
        }
        return a1;
    }
}

int main() {
    int a0, a1, p, q, k;
    while (cin >> a0 >> a1 >> p >> q >> k) {
        cout << get_a(a0, a1, p, q, k) << endl;
    }
}

法2:转换为矩阵快速幂

略,不想学他了,直接复制了代码

#include<iostream>
#include <cstring>

using namespace std;

// 递推公式构造的矩阵大小为 2*2
const int MAXN = 2;
const int mod = 10000;

// 矩阵类,通用
struct Matrix{
    int matrix[MAXN][MAXN];
    int row, col;
    Matrix(int r, int c):row(r),col(c){
        // 构造函数内将矩阵元素全部初始化为0
        memset(matrix, 0, sizeof(matrix));
    };
};

// 计算矩阵乘法
Matrix Multiply(Matrix x, Matrix y){
    Matrix answer(x.row, y.col);
    for(int i = 0; i<answer.row; i++){
        for(int j = 0; j<answer.col; j++){
            for(int k = 0; k<x.col; k++){
                // 注意中间结果也要进行取模运算
                answer.matrix[i][j] = (answer.matrix[i][j] + x.matrix[i][k] * y.matrix[k][j]) % mod;
            }
        }
    }
    return answer;
}

// 构造单位矩阵初始化快速幂结果矩阵
Matrix InitAnswer(Matrix x){
    Matrix answer(x.row, x.col);    // 元素已全部初始化为0
    for(int i=0;i<answer.row;i++)
        answer.matrix[i][i] = 1;            // 对角线元素初始化为1,构造单位矩阵
    return answer;
}

// 核心函数:计算矩阵快速幂
Matrix FastExponentiation(Matrix x, int k){
    Matrix answer = InitAnswer(x);
    while(k>0){
        if(k%2==1)    // k为奇数则answer累乘当前x
            answer = Multiply(answer, x);
        // 矩阵平方,指数减半
        x = Multiply(x, x);
        k /= 2;   // 等价于右移一位 k>>=1;
    }
    return answer;
}

int main() {
    int a0, a1, p, q, k;
    while (cin >> a0 >> a1 >> p >> q >> k) {
        // 构造参数矩阵A
        Matrix answer(MAXN, MAXN);
        answer.matrix[0][0] = p, answer.matrix[0][1] = q;
        answer.matrix[1][0] = 1, answer.matrix[1][1] = 0;
        // 利用矩阵快速幂对参数矩阵A求解k-1次幂
        answer = FastExponentiation(answer, k - 1);
        // A的第一行乘以起始元素列向量 [a1, a0]' 即为an的值,注意大数需要随时取模
        int res = ((answer.matrix[0][0] * a1) % mod + (answer.matrix[0][1] * a0) % mod) % mod;
        cout << res << endl;
    }
    return 0;
}
最后修改:2023 年 06 月 12 日
如果觉得我的文章对你有用,请随意赞赏