机试_4_数学问题

在机试中,我们经常会面对这样一类问题,它们并不涉及很深奥的算法和数据结构,而只与数理逻辑相关,将这类题目称为数学问题。

这类问题通常不需要用到特别高深的数学知识,而只需要掌握简单的数理逻辑知识。本文重点记录机试中常常涉及的一系列数学问题,以便很好地掌握计算机考研机试中涉及的数学问题的求解。

一、进制转换

三种题型:
1. 十进制转N进制(10 --> N)
2. M进制转10进制(M --> 10)
3. M进制转N进制(M --> N)

1、二进制数–北京邮电大学

描述:

大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。

输入描述:

多行,每一行表示要求的数字

输出描述:

输出共T行。每行输出求得的二进制串。

示例1:

输入:

23
535
2624
56275
989835

输出:

10111
1000010111
101001000000
1101101111010011
11110001101010001011

题解:

分析:
十进制正数转二进制,使用"除2取余法"即可。

我们使用一个数组来存放 num % 2 的值,然后再将num / 2,以此循环,直至num为0。
最后逆序输出数组即可。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;

/**
 * 将整数转划为二进制
 * @param num
 */
void convertToBinary(int num);


/**
 * 二进制数--北京邮电大学
 * @return
 */
int main() {
    int num;
    while (cin >> num) {
        convertToBinary(num);
    }

    return 0;
}

void convertToBinary(int num) {
    vector<int> countVector;
    if (num == 0) {
        //特殊情况,num为0
        countVector.push_back(0);
    } else {
        while (num != 0) {
            countVector.push_back(num % 2);
            //右移1位相当于除2
            num = num >> 1;
        }
    }

    /*
     * 逆序输出
     */
    for (int i = countVector.size() - 1; i >= 0; --i) {
        cout << countVector[i];
    }
    cout << endl;
}

2、八进制–华中科技大学

描述:

输入一个整数,将其转换成八进制数输出。

输入描述:

输入包括一个整数N(0<=N<=100000)。

输出描述:

可能有多组测试数据,对于每组数据, 输出N的八进制表示数。

示例1

输入:

7
8
9

输出:

7
10
11

题解:

分析:
十进制正数转八进制,使用"除8取余法"即可。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;


/**
 * 将十进制整数转化为n进制
 * @param num
 * @param n
 */
void convertTen2N(int num, int n);


/**
 * 八进制数--华中科技大学
 * @return
 */
int main() {
    int num;
    while (cin >> num) {
        //转八进制
        convertTen2N(num, 8);
    }

    return 0;
}

void convertTen2N(int num, int n) {
    vector<int> countVector;
    if (num == 0) {
        //特殊情况,num为0
        countVector.push_back(0);
    } else {
        while (num != 0) {
            countVector.push_back(num % n);
            num /= n;
        }
    }

    /*
     * 逆序输出
     */
    for (int i = countVector.size() - 1; i >= 0; --i) {
        cout << countVector[i];
    }
    cout << endl;
}

3、进制转换–北京大学

描述:

写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。

输入描述:

输入一个十六进制的数值字符串。

输出描述:

输出该数值的十进制字符串。

示例1

输入:

0xA

输出:

10

题解:

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * char转int
 * @param target
 * @return
 */
int char2Int(char target);

/**
 * 将m进制的num转化为十进制
 * @param num
 * @param m
 */
void convertM2Ten(string num, int m);

/**
 * 进制转换--北京大学
 * @return
 */
int main() {
    string hexNum;
    while (cin >> hexNum) {
        hexNum = hexNum.substr(2);
        //16进制转10进制
        convertM2Ten(hexNum, 16);
    }

    return 0;
}

int char2Int(char target) {
    if ('0' <= target && target <= '9') {
        //0~9直接减去字符0即可
        return target - '0';
    } else {
        //大于等于10,则先减去字符A(注意题目是大写还是小写),再加上10
        return target - 'A' + 10;
    }
}

void convertM2Ten(string num, int m) {
    int number = 0;

    /*
     * 解法1
     */
    for (int i = 0; i < num.size(); ++i) {
        number *= m;
        number += char2Int(num[i]);
    }

    /*
     * 解法2:利用基数乘以权重
     */
//    int x = 1;
//    for (int i = num.size() - 1; i >= 0; --i) {
//        number += char2Int(num[i]) * x;
//        x *= m;
//    }
    cout << number << endl;
}


4、进制转换2–清华大学

描述:

将M进制的数X转换为N进制的数输出。

输入描述:

输入的第一行包括两个整数:M和N(2<=M,N<=36)。
下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成N进制的数输出。

输出描述:

输出X的N进制表示的数。

示例1

输入:

10 2
11

输出:

1011

备注:

注意输入时如有字母,则字母为大写,输出时如有字母,则字母为小写。

题解:

分析:
上面的题都懂了之后,综合起来就是本题的解法了。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;

/**
 * char转int
 * @param target
 * @return
 */
int char2Int(char target);

/**
 * int转char
 * @param target
 * @return
 */
char int2Char(int target);

/**
 * 将m进制的num转化为十进制
 * @param num
 * @param m
 */
long long convertM2Ten(string num, int m);

/**
 * 将十进制的num转化为n进制
 * @param num
 * @param n
 */
void convertTen2N(long long num, int n);

/**
 * 进制转换2--清华大学
 * @return
 */
int main() {
    int m, n;
    while (cin >> m >> n) {
        string num;
        cin >> num;
        long long number = convertM2Ten(num, m);
        convertTen2N(number, n);
    }

    return 0;
}

long long convertM2Ten(string num, int m) {
    long long number = 0;
    for (int i = 0; i < num.size(); ++i) {
        number *= m;
        number += char2Int(num[i]);
    }
    return number;
}

void convertTen2N(long long num, int n) {
    vector<char> ans;
    if (num == 0) {
        //特殊情况
        ans.push_back('0');
    } else {
        while (num != 0) {
            ans.push_back(int2Char(num % n));
            num /= n;
        }
    }

    for (int i = ans.size() - 1; i >= 0; --i) {
        cout << ans[i];
    }
    cout << endl;
}

char int2Char(int target) {
    if (target < 10) {
        return target + '0';
    } else {
        //题目备注已经说明,输出为小写字母
        return target - 10 + 'a';
    }
}

int char2Int(char target) {
    if ('0' <= target && target <= '9') {
        //0~9直接减去字符0即可
        return target - '0';
    } else {
        //大于等于10,则先减去字符A(注意题目是大写还是小写),再加上10
        return target - 'A' + 10;
    }
}


二、GCD & LCM

1.最大公约数
最大公约数(Greatest Common Divisor,GCD)是指两个或多个整数共有约数中,最大的一个约数。
求最大公约数最常用的方法是欧几里得算法,又称辗转相除法。

若整数g为a,b(a和b不同时为0)的公约数,则g满足 a = g × l、b = g × m
其中,l,m为整数。同时,a又可由b表示为 a = b × k + n
其中,k为整数,r为α除以b后的余数。对以上三个公式做如下变形:
		g × l = g × m × k + r
		r = g × (l - m × k), (g ≠ 0)
由上式可知,a,b的公约数g可以整除a除以b后的余数r(记为a mod b)。即(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等。

这样,把求a,b的最大公约数转换成了求b,a mod b的最大公约数,问题不变而数据规模明显变小。
通过不断重复该过程,直到问题缩小成求某个非零数与零的最大公约数。
这样,该非零数即是所求。
2.最小公倍数
最小公倍数(Least Common Multiple,LCM)是指要两个或多个整数公有的倍数中,除0之外最小的那一个公倍数。
α,b两个数的最小公倍数为两数的乘积除以它们的最大公约数。

1、最大公约数–哈尔滨工业大学

描述:

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

输入描述:

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

输出描述:

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

示例1:

输入:

49 14

输出:

7

题解:

解法1:暴力枚举
假设两个数a、b,且a > b。
我们a开始,以此递减,若 a % i == 0 && b % i == 0,则此时的i即为最大公约数。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 求x、y的最大公约数
 * @param x
 * @param y
 * @return
 */
int getGCD(int x, int y);

int getMax(int x, int y);

/**
 * 最大公约数--哈尔滨工业大学
 * @return
 */
int main() {
    int x, y;
    while (cin >> x >> y) {
        cout << getGCD(x, y) << endl;
    }

    return 0;
}

int getGCD(int x, int y) {
    int max = getMax(x, y);
    for (int i = max; i >= 0; --i) {
        if (x % i == 0 && y % i == 0) {
            return i;
        }
    }
    return -1;
}

int getMax(int x, int y) {
    return x >= y ? x : y;
}

解法2:辗转相除法(欧几里得算法)
假设求 GCD(3139, 2117)
1. 3139 % 2117 = 1022
2. 2117 % 1022 = 73
3. 1022 % 73 = 0

则,GCD(3139, 2117) = 73
即,73为3139和2117的最大公约数

至于代码,可以基于递归进行实现。
递归出口是GCD(x, y)中的y等于0。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 求x、y的最大公约数--递归实现
 * @param x
 * @param y
 * @return
 */
int getGCD(int x, int y);


/**
 * 最大公约数--哈尔滨工业大学
 * @return
 */
int main() {
    int x, y;
    while (cin >> x >> y) {
        cout << getGCD(x, y) << endl;
    }

    return 0;
}

int getGCD(int x, int y) {
    //递归出口
    if (y == 0) {
        return x;
    }
    return getGCD(y, x % y);
}


2、最小公倍数–杭州电子科技

描述:

输入两个正整数,求其最小公倍数。

输入描述:

测试数据有多组,每组只有一行,包括两个不大于1000的正整数。

输出描述:

对于每组输入,请输出其最小公倍数,每个实例输出一行。

示例1:

输入:

49 14

输出:

7

题解:

解法1:暴力枚举
LCM(x, y)
从1 ~ (x*y)开始枚举,若满足 (i % x == 0 && i % y == 0),则i即可x、y的最小公倍数。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 求x、y的最小公倍数
 * @param x
 * @param y
 * @return
 */
int getLCM(int x, int y);

/**
 * 最小公倍数--杭州电子科技大学
 * @return
 */
int main() {
    int x, y;
    while (cin >> x >> y) {
        cout << getLCM(x, y) << endl;
    }

    return 0;
}

int getLCM(int x, int y) {
    int max = x * y;
    for (int i = 1; i <= max; ++i) {
        if (i % x == 0 && i % y == 0) {
            return i;
        }
    }
    return -1;
}
解法2:数学公式
LCM(x, y) = (x * y) / GCD(x, y);
其中,GCD(x, y)表示x、y的最大公约数。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 求x、y的最小公倍数
 * @param x
 * @param y
 * @return
 */
int getLCM(int x, int y);

/**
 * 求x、y的最大公约数--递归实现
 * @param x
 * @param y
 * @return
 */
int getGCD(int x, int y);

/**
 * 最小公倍数--杭州电子科技大学
 * @return
 */
int main() {
    int x, y;
    while (cin >> x >> y) {
        cout << getLCM(x, y) << endl;
    }

    return 0;
}

int getLCM(int x, int y) {
    return (x * y) / getGCD(x, y);
}

int getGCD(int x, int y) {
    //递归出口
    if (y == 0) {
        return x;
    }
    return getGCD(y, x % y);
}

三、质数

质数: 也称素数,是指只能被其自身和1整除的正整数。

如何判断一个数是否为素数呢?
可以用所有小于该数的正整数去试着整除该数,若存在某个数能够整除该数,则该数不是素数:若这些数都不能整除它,则该数为素数。
算法的时间复杂度为O(n)。

然而,并不用测试到 n-1,而只需测试到sqrt(n),即√n的整数即可.
	若到这个整数为止,所有正整数均不能整除n,则可以断定n为素数。
	若n不存在大于sqrt(n)的因数,则该做法显然正确:
如此一来,测试一个数是否是素数的复杂度就从O(n)降至了O(sqrt(n))。
分解质因数
素数的应用之一就是应用在分解质因数。
每个数都可以写成一个或几个质数相乘的形式,其中每个质数都是这个数的质因数。
例如:
30 = 2 * 3 * 5
120 = 2 * 2 * 2 * 3 * 5

1、素数判断–哈尔滨工业大学

描述

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

输入描述:

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

输出描述:

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

示例1

输入:

13

输出:

yes

题解:

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cmath>

using namespace std;

/**
 * 判断一个数是否为素数
 * @param num
 * @return
 */
bool isPrimeNum(int num);

/**
 * 素数判定--哈尔滨工业大学
 * @return
 */
int main() {
    int num;
    while (cin >> num) {
        if (isPrimeNum(num)) {
            cout << "yes" << endl;
        } else {
            cout << "no" << endl;
        }
    }

    return 0;
}


bool isPrimeNum(int num) {
    if (num <= 1) {
        return false;
    }
    //sqrt()比较耗时,所以提前定义一个变量进行赋值
    int bound = sqrt(num);
    for (int i = 2; i <= bound; ++i) {
        if (num % i == 0) {
            return false;
        }
    }
    return true;
}

2、素数–北京航空航天大学

描述

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

输入描述:

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

输出描述:

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

示例1

输入:

100

输出:

11 31 41 61 71

题解:

解法:素数筛法
算法思想:
找到一个素数,就将它的所有倍数均标记成非素数。这样一来,当遍历到一个数时,若它未被任何小于它的素数标记为非素数,则确定其为素数。

步骤如下:
从2到区间端点开始遍历,若当前整数(假设为A),没有因为一个小于A的素数的倍数而被标记为非素数时,则判断A为素数,同时标记A的整数倍为非素数。
继续遍历下一个数,以此循环,直到遍历完题设的区间。
此时,所有未被标记成非素数的数即为要求的素数。

解题思路:
在处理输入的数据之前,可以先利用素数筛法求出2到10000内的所有素数。
然后得到输入n之后,依次判断在1~n这个区间内的素数是否符合题目条件,若符合则输出盖素数,否则继续判断下一个素数,直至该区间内所有的素数都判断完为止。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;

const int MAX_NUM = 10001;

/**
 * 保存素数
 */
vector<int> primeNum;

/**
 * 标记素数
 */
bool isPrimeNum[MAX_NUM];

/**
 * 初始化 isPrimeNum[]数组
 */
void init();

/**
 * 素数--北京航空航天大学
 * @return
 */
int main() {
    init();
    int n;
    while (cin >> n) {
        bool isOutput = false;
        for (int i = 1; i < n; ++i) {
            /*
             * i % 10 == 1表示个位为1,且i为素数,则输出
             */
            if (i % 10 == 1 && isPrimeNum[i]) {
                isOutput = true;
                cout << i << " ";
            }
        }
        if (!isOutput) {
            cout << "-1";
        }
        cout << endl;
    }

    return 0;
}

void init() {
    //初始化
    for (int i = 0; i < MAX_NUM; ++i) {
        isPrimeNum[i] = true;
    }
    /*
     * 0、1都不是素数
     */
    isPrimeNum[0] = false;
    isPrimeNum[1] = false;
    //从2开始遍历
    for (int i = 2; i < MAX_NUM; ++i) {
        if (!isPrimeNum[i]) {
            //数字i为非素数,则直接跳过
            continue;
        } else {
            //数字i为素数
            primeNum.push_back(i);
            /*
             * 若 i * i > MAX_NUM,则不用再循环了
             */
            if (i > MAX_NUM / i) {
                continue;
            }
            /*
             * 素数的整数倍为非素数
             * 直接从 i * i 开始循环,减少重复的工作,同时j += i,以保证j是i的整数倍
             * 为何从i*i开始呢?
             * 因为,i*k(其中k<i)必定已经在求k的某个素因数(必定小于i)时被标记过了,即i*k同时也是k的素因数的倍数。
             * 举例,标记素数3的倍数是,3 * 2 = 6,已经在标记素数2的倍数时被标记了,因此我们直接从3 * 3 = 9开始标记
             */
            for (int j = i * i; j < MAX_NUM; j += i) {
                isPrimeNum[j] = false;
            }
        }
    }
}


3、质因数的个数–清华大学

描述:

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

输入描述:

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

输出描述:

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

示例1

输入:

120

输出:

5

题解:

本题是将输入的整数进行分解素因数,并计算每个素因数对应的幂指数之和。

思路分析:
我们先用素数筛法,预先筛选出所有可能在题设所给的数据范围内出现德素数,然后接收程序的输入n,利用短除法求解,即从2开始依次遍历所有小于n的素数(记为A),并判断其是否为n的因数。
若是,则用 n = n / A,继续循环。
若否,则继续循环。

算法实现:
1. 利用素数筛法筛选出[0, MAX_NUM]内的所有素数。
   注:MAX_NUM = sqrt(数据规模)即可,并不需要等于数据规模。
   令sqrt(n)为x,因为一个整数n,之多只会存在一个大于x的素因数(原因:两个大于x的数相乘必会大于n)。
   因此,将所有小于x的素因数从n去除,剩余部分必为大于x的素因数,且若存在大于x的素因数,其幂指数必为1。
2. 出入n
3. 从2开始测试素数(记为A)能否整除n。若能,则表明A是n的一个素因数。
4. 循环3,直到不能在被整除为止,同时统计其幂指数。
5. 遍历完预处理出来的素数后,
   若n为1,则表明n的所有素因数全部被分解出来。
   若n不为1,则表明n存在一个大于MAX_NUM的因子,该因子必为其素因子,且幂指数必然为1。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>

using namespace std;

const int MAX_NUM = 4e4;

/**
 * 保存素数
 */
vector<int> primeNum;

/**
 * 标记素数
 */
bool isPrimeNum[MAX_NUM];

/**
 * 初始化 isPrimeNum[]数组
 */
void init();

/**
 * 质因数的个数--清华大学
 * @return
 */
int main() {
    init();
    int n;
    while (cin >> n) {
        int count = 0;
        for (int i = 0; i < primeNum.size() && primeNum[i] < n; ++i) {
            int factor = primeNum[i];
            //n能整除质因数
            while (n % factor == 0) {
                n /= factor;
                count++;
            }
        }
        //还存在一个大于sqrt(n)的质因数
        if (n > 1) {
            count++;
        }
        cout << count << endl;
    }

    return 0;
}

void init() {
    //初始化
    for (int i = 0; i < MAX_NUM; ++i) {
        isPrimeNum[i] = true;
    }
    /*
     * 0、1都不是素数
     */
    isPrimeNum[0] = false;
    isPrimeNum[1] = false;
    //从2开始遍历
    for (int i = 2; i < MAX_NUM; ++i) {
        if (!isPrimeNum[i]) {
            //数字i为非素数,则直接跳过
            continue;
        } else {
            //数字i为素数
            primeNum.push_back(i);
            /*
             * 若 i * i > MAX_NUM,则不用再循环了
             */
            if (i > MAX_NUM / i) {
                continue;
            }
            /*
             * 素数的整数倍为非素数
             * 直接从 i * i 开始循环,减少重复的工作,同时j += i,以保证j是i的整数倍
             * 为何从i*i开始呢?
             * 因为,i*k(其中k<i)必定已经在求k的某个素因数(必定小于i)时被标记过了,即i*k同时也是k的素因数的倍数。
             * 举例,标记素数3的倍数是,3 * 2 = 6,已经在标记素数2的倍数时被标记了,因此我们直接从3 * 3 = 9开始标记
             */
            for (int j = i * i; j < MAX_NUM; j += i) {
                isPrimeNum[j] = false;
            }
        }
    }
}

四、快速幂

快速幂:指快速求得a的b次方的方法。

在这里插入图片描述


1、人见人爱的A^B

描述

求AB的最后三位数表示的整数。说明:AB的含义是“A的B次方”。

输入描述:

输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1 <= A,B <= 10000)。
如果A=0,B=0,则表示输入数据的结束,不做处理。

输出描述:

对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。

示例1

输入:

2 3
12 6
6789 10000
0 0

输出:

8
984
1

题解:

本题的一个小坑,就是求一个数的后三位数。

最简单的方法:该数对1000取模即可。
但是在本例中,是否可以先求得A^B的具体数字后再求其后三位数呢?
显然,不行。

按照题面给出的输入规模,A^B至多可以达到10000的10000次方,这么庞大的数字是不容易存储的,但A^B的后三位数其实只与A的后三位数和B有关。

由于题目要求的只是结果的后三位数,因此在计算该结果的过程中产生的中间值也只需保存其后三位数即可。即在利用快速幂求A^B的计算过程中,只需不断地将中间结果对1000取模。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

const int MOD = 1000;

/**
 * 快速幂求a^b
 * @param a
 * @param b
 * @return
 */
int quickPower(int a, int b);

/**
 * 人见人爱的A^B--杭州电子科技大学
 * @return
 */
int main() {
    int a, b;
    while (cin >> a >> b) {
        if (a == 0 && b == 0) {
            break;
        }
        cout << quickPower(a, b) << endl;
    }

    return 0;
}

int quickPower(int a, int b) {
    int ans = 1;
    while (b != 0) {
        if (b % 2 == 1) {
            ans *= a;
            //求后三位数
            ans %= MOD;
        }
        b = b >> 1;
        //a不断平方
        a *= a;
        //求后三位数
        a %= MOD;
    }
    return ans;
}

五、矩阵与矩阵快速幂

1. 矩阵
数学问题中另外一个常考的知识点是矩阵。
考查的内容不难,一般只考查矩阵的基本运算,如矩阵加法、矩阵乘法、矩阵转置等。
通常,可用一个二维数组来模拟矩阵。

2. 矩阵快速幂
类似于普通快速幂,矩阵的快速幂就是高效求矩阵多幂次的方法,其思想和普通快速幂的思想相同。
唯一不同的地方在于,对数字的快速幂而言,其初始值是1;而对于矩阵快速幂而言,其初始值是单位矩阵。
/**
 * 矩阵操作,包括
 * 1.矩阵加法(减法同理)
 * 2.矩阵乘法
 * 3.矩阵转置
 * 4.矩阵求幂
 */
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 矩阵维数
 */
const int DIMENSION = 3;

/**
 * 矩阵
 */
struct Matrix {
    int row;    //矩阵的行
    int col;    //矩阵的列
    int matrix[DIMENSION][DIMENSION];     //二维数组模拟矩阵
    //构造函数
    Matrix(int row, int col) {
        this->row = row;
        this->col = col;
    }
};

/**
 * 矩阵加法
 * @param x
 * @param y
 * @return
 */
Matrix add(Matrix x, Matrix y);

/**
 * 矩阵乘法
 * @param x
 * @param y
 * @return
 */
Matrix multiply(Matrix x, Matrix y);

/**
 * 矩阵转置
 * @param x
 * @return
 */
Matrix transpose(Matrix x);

/**
 * 矩阵求幂
 * @param x
 * @return
 */
Matrix quickPower(Matrix x, int n);

/**
 * 打印矩阵
 * @param x
 */
void printMatrix(Matrix x);

/**
 * 矩阵操作
 * @return
 */
int main() {
    /*
     * 自行测试矩阵操作
     */
    return 0;
}

Matrix add(Matrix x, Matrix y) {
    Matrix ans = Matrix(x.row, x.col);
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            //对应位置相加即可
            ans.matrix[i][j] = x.matrix[i][j] + y.matrix[i][j];
        }
    }
    return ans;
}

Matrix multiply(Matrix x, Matrix y) {
    //矩阵乘法结果是x的行和y的列
    Matrix ans(x.row, y.col);
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            //先初始化为0
            ans.matrix[i][j] = 0;
            /*
             * 根据矩阵相乘的规则
             * matrix[i][j] = x的第i行与y的第j列对应的乘积和
             */
            for (int k = 0; k < x.col; ++k) {
                ans.matrix[i][j] += x.matrix[i][k] * y.matrix[k][j];
            }
        }
    }
    return ans;
}

void printMatrix(Matrix x) {
    //注:特别注意题目明确的输出格式
    for (int i = 0; i < x.row; ++i) {
        for (int j = 0; j < x.col; ++j) {
            if (j == 0) {
                //第一列不带空格
                cout << x.matrix[i][j];
            } else {
                //非第一列带前置空格
                cout << " " << x.matrix[i][j];
            }
        }
        cout << endl;
    }
}

Matrix transpose(Matrix x) {
    Matrix ans = Matrix(x.col, x.row);
    for (int i = 0; i < x.row; ++i) {
        for (int j = 0; j < x.col; ++j) {
            ans.matrix[i][j] = x.matrix[j][i];
        }
    }
    return ans;
}

Matrix quickPower(Matrix x, int n) {
    Matrix ans = Matrix(x.row, x.col);
    //初始化为单位阵
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            if (i == j) {
                ans.matrix[i][j] = 1;
            } else {
                ans.matrix[i][j] = 0;
            }
        }
    }

    /*
     * 求快速幂的方法
     */
    while (n != 0) {
        if (n % 2 == 1) {
            ans = multiply(ans, x);
        }
        n = n >> 1;
        x = multiply(x, x);
    }
    return ans;
}

1、计算两个矩阵的乘积–哈尔滨工业大学

描述

计算两个矩阵的乘积,第一个是2 x 3,第二个是3 x 2。

输入描述:

输入为两个矩阵,其中一个为2 x 3的矩阵,另一个为3 x 2的矩阵。

输出描述:

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

示例1

输入:

1 2 3
3 4 5
6 7
8 9
10 11

输出:

52 58
100 112

题解:

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 矩阵维数
 */
const int DIMENSION = 3;

/**
 * 矩阵
 */
struct Matrix {
    int row;    //矩阵的行
    int col;    //矩阵的列
    int matrix[DIMENSION][DIMENSION];     //二维数组模拟矩阵

    //构造函数
    Matrix(int row, int col) {
        this->row = row;
        this->col = col;
    }
};

/**
 * 矩阵乘法
 * @param x
 * @param y
 * @return
 */
Matrix multiply(Matrix x, Matrix y);

/**
 * 打印矩阵
 * @param x
 */
void printMatrix(Matrix x);

/**
 * 计算两个矩阵的乘积--哈尔滨工业大学
 * @return
 */
int main() {
    Matrix x = Matrix(2, 3);
    Matrix y = Matrix(3, 2);
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            cin >> x.matrix[i][j];
        }
    }
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 2; ++j) {
            cin >> y.matrix[i][j];
        }
    }
    Matrix ans = multiply(x,y);
    printMatrix(ans);

    return 0;
}

Matrix multiply(Matrix x, Matrix y) {
    //矩阵乘法结果是x的行和y的列
    Matrix ans(x.row,y.col);
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            //先初始化为0
            ans.matrix[i][j] = 0;
            /*
             * 根据矩阵相乘的规则
             * matrix[i][j] = x的第i行与y的第j列对应的乘积和
             */
            for (int k = 0; k < x.col; ++k) {
                ans.matrix[i][j] += x.matrix[i][k] * y.matrix[k][j];
            }
        }
    }
    return ans;
}

void printMatrix(Matrix x) {
    for (int i = 0; i < x.row; ++i) {
        for (int j = 0; j < x.col; ++j) {
            cout << x.matrix[i][j] << " ";
        }
        cout << endl;
    }
}


2、矩阵幂–北京邮电大学

描述

给定一个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列个整数,每行数之间用空格隔开,注意,每行最后一个数后面不应该有多余的空格。

示例1

输入:

2 2
9 8
9 3
3 3
4 8 4
9 3 0
3 5 7
5 2
4 0 3 0 1
0 0 5 8 5
8 9 8 5 3
9 6 1 7 8
7 2 5 7 3

输出:

153 96
108 81
1216 1248 708
1089 927 504
1161 1151 739
47 29 41 22 16
147 103 73 116 94
162 108 153 168 126
163 67 112 158 122
152 93 93 111 97

题解:

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

/**
 * 矩阵维数
 */
const int DIMENSION = 10;

/**
 * 矩阵
 */
struct Matrix {
    int row;    //矩阵的行
    int col;    //矩阵的列
    int matrix[DIMENSION][DIMENSION];     //二维数组模拟矩阵

    //构造函数
    Matrix(int row, int col) {
        this->row = row;
        this->col = col;
    }
};

/**
 * 矩阵乘法
 * @param x
 * @param y
 * @return
 */
Matrix multiply(Matrix x, Matrix y);

/**
 * 矩阵求幂
 * @param x
 * @return
 */
Matrix quickPower(Matrix x, int n);

/**
 * 打印矩阵
 * @param x
 */
void printMatrix(Matrix x);


/**
 * 矩阵幂--北京邮电大学
 * @return
 */
int main() {
    int n;
    int k;
    while (cin >> n >> k) {
        Matrix x = Matrix(n, n);
        for (int i = 0; i < x.row; ++i) {
            for (int j = 0; j < x.col; ++j) {
                cin >> x.matrix[i][j];
            }
        }
        Matrix ans = quickPower(x, k);
        printMatrix(ans);
    }
    return 0;
}

Matrix multiply(Matrix x, Matrix y) {
    //矩阵乘法结果是x的行和y的列
    Matrix ans(x.row, y.col);
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            //先初始化为0
            ans.matrix[i][j] = 0;
            /*
             * 根据矩阵相乘的规则
             * matrix[i][j] = x的第i行与y的第j列对应的乘积和
             */
            for (int k = 0; k < x.col; ++k) {
                ans.matrix[i][j] += x.matrix[i][k] * y.matrix[k][j];
            }
        }
    }
    return ans;
}

void printMatrix(Matrix x) {
    //注意题目明确的输出格式
    for (int i = 0; i < x.row; ++i) {
        for (int j = 0; j < x.col; ++j) {
            if (j == 0) {
                //第一列不带空格
                cout << x.matrix[i][j];
            } else {
                //非第一列带前置空格
                cout << " " << x.matrix[i][j];
            }
        }
        cout << endl;
    }
}

Matrix quickPower(Matrix x, int n) {
    Matrix ans = Matrix(x.row, x.col);
    //初始化为单位阵
    for (int i = 0; i < ans.row; ++i) {
        for (int j = 0; j < ans.col; ++j) {
            if (i == j) {
                ans.matrix[i][j] = 1;
            } else {
                ans.matrix[i][j] = 0;
            }
        }
    }

    /*
     * 求快速幂的方法
     */
    while (n != 0) {
        if (n % 2 == 1) {
            ans = multiply(ans, x);
        }
        n = n >> 1;
        x = multiply(x, x);
    }

    return ans;
}

六、高精度整数

最大的整型long long的范围是[-9223372036854775808, 9223372036854775807],即使是无符号位的 unsigned long long 最大值也只是扩大一倍。

在某些例子中,可以利用各种技巧回避了直接保存这种整数。但在有些问题中,不得不保存并处理数值巨大的整数。

那么该如何处理呢?这就是本节要讨论的内容——高精度整数(大整数)。

若机试允许使用Java的UU,直接跳过即可,因为Java类库中内置了BigInteger类,了解其用法然后尝试解题即可。

若机试允许使用Python的UU,直接无视即可,因为Python中整型不限制长度。

若机试只允许使用C\C++的UU,只能自定义结构体或者类来模拟大整数。

篇幅所限,另写博客记录。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java华为必备函数主要包括以下几个方面: 1. 输入输出函数:在中通常需要从标准输入读取数据,并将计算结果输出到标准输出。常用的输入输出函数包括Scanner类的next()、nextInt()等方法用于读取数据,以及System.out.println()等方法用于输出计算结果。 2. 字符串操作函数:字符串操作是中常见的任务之一,比如字符串的拼接、截取、查找等。Java中提供了许多字符串操作函数,如String类的concat()、substring()、indexOf()等方法,可以满足处理字符串的需求。 3. 数组操作函数:中经常需要对数组进行操作,包括数组的排序、查找、筛选等。Java中提供了Arrays类,其中包含了许多方便的数组操作函数,如sort()、binarySearch()、copyOf()等方法,可以简化对数组的处理过程。 4. 数据结构函数:中常用的数据结构包括栈、队列、链表、二叉树等,对于这些数据结构的操作往往需要使用相应的函数。Java中提供了许多数据结构相关的类和接口,如Stack类、Queue接口、LinkedList类、TreeNode类等,可以方便地进行数据结构的操作。 5. 数学函数:中会涉及到一些数学计算,比如取模、求平方根、求最大公约数等。Java中提供了Math类,其中包含了许多数学计算相关的函数,如abs()、sqrt()、gcd()等方法,可以进行常见的数学计算。 除了上述函数外,还需要熟悉Java的基本语法和面向对象的思想,以及常见的算法和数据结构,这样才能在华为中取得较好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

窝在角落里学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值