【考研机试刷题】6 数学问题

6 数学问题

机试中的数学问题,不涉及深奥的算法和数据结构,只与数理逻辑相关,这里包括了:
进制转换、最大公约数、最小公倍数、质数、分解质因数、快速幂、矩阵与矩阵快速幂、高精度整数

1 进制转换

有3种,10进制转n进制、m进制转10进制、m进制转n进制,例题和习题的顺序也是这样排列的。

例题6.1 二进制数(北邮复试上机)

题目

这道题为10进制转2进制

题目地址

题解
#include <vector>
#include <cstdio>
using namespace std;

int main(){
    unsigned int num;
    while(scanf("%d",&num)!=EOF){
        vector<int> binary; //由于是“变长数组”,用vector
        while(num!=0){
            binary.push_back(num%2);    //%2不会产生0,不会将0 push到binary中
            num /=2;
        }

        //由于binary中低位在前,所以要将binary逆序输出才有正常的二进制数
        for(int i = binary.size()-1;i>=0;i--)
        {
            printf("%d",binary[i]);
        }
        printf("\n");
    }
}
笔记
  1. 二进制位数未知,所以用vector当变长数组用
  2. num!=0,所以不用担心num%2会产生0,不会将0放入binary中,也就不会有二进制串的前导0
  3. 十进制转换二进制过程中,先产生的是原二进制中的低位,所以结果要逆序输出

习题6.1 八进制(华中科技大学复试上机)

题目

这道题将上一道题的方法扩展为10进制转n进制

题目地址

题解
#include <cstdio>
#include <string>
#include <vector>

using namespace std;

//十进制转化为n进制
vector<int> ConvertT2N(int number, int n){
    vector<int> answer;
    if(number == 0){
        answer.push_back(0);    //单独处理0的情况
    }
    else{
        while(number != 0){
            //转换为n进制,所以对n做计算
            answer.push_back(number%n);
            number /= n;
        }
    }

    return answer;
}

int main(){
    int number;
    while(scanf("%d",&number)!=EOF){
        vector<int> answer = ConvertT2N(number,8);   //本题为转化为8进制
        
        //逆序输出
        for(int i = answer.size()-1;i>=0;i--){
            printf("%d",answer[i]);
        }
        printf("\n");
    }
}
笔记
  1. 可以把10进制转换为2进制,单独写成一个函数,且可以泛化为求n进制,但是这里的限制是n必须小于10
  2. 单独处理number为0的情况

习题6.1扩展 十进制转化为16进制

上一道题虽然将10进制转2进制扩展为转n进制,但不允许n大于10。
本题通过int与char的转换,实现10进制转换为16进制

题解
#include <cstdio>
#include <string>
#include <vector>

using namespace std;

//当目标进制可能大于10时,选择统一用字符
char Int2Char(int target){
    if(target<10){
        return target + '0';    //如果目标进制小于10,则返回数字对应的字符
    }
    else{
        return target - 10 + 'A';   //如果目标进制大于10,则返回对应大写字母
    }
}

//十进制转化为n进制,这里n可能大于10
vector<char> ConvertT2N(int number, int n){
    vector<char> answer;
    if(number == 0){
        answer.push_back('0');    //统一用字符
    }
    else{
        while(number != 0){
            //转换为n进制,n可能大于10时,只有计算时用的int,其他都用char
            answer.push_back(Int2Char(number%n));   //统一用字符
            number /= n;
        }
    }

    return answer;
}

int main(){
    int number;
    while(scanf("%d",&number)!=EOF){
        vector<char> answer = ConvertT2N(number,16);   //上一题为转化为8进制
        
        //逆序输出
        for(int i = answer.size()-1;i>=0;i--){
            printf("%c",answer[i]); //当目标进制大于10,统一用字符输出
        }
        printf("\n");
    }
}
笔记
  1. 10进制转换为n进制,n可能大于10时,统一用字符vector保存数位,并用字符%c输出
  2. Int2Char函数的写法

习题6.3 进制转换(北京大学复试上机)

题目

接下来是m进制转换为10进制,本题为16进制转换为10进制
题目地址

题解
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;

//n进制转换为10进制,输入的是字符串,所以想拿来计算前要转换成int
int Char2Int(char target){
    if('0' <= target && target <= '9'){
        return target - '0';    //当原字符为数字的字符时,加'0'可得到数字本身
    }
    else {
        return target - 'A' + 10;   //当原字符不是数字,即十六进制10-15的数
    }
}

//m进制转换为10进制
int ConvertM2T(string str,int n){
    int number = 0;
    for(int i = 0;i<str.size();++i){
        //先让高位的str[0]乘基数,顺序没错
        number *= n;
        number += Char2Int(str[i]);
    }
    return number;
}

int main(){
    string str;
    while(cin>>str){
        str = str.substr(2);    //排除掉16进制前面的标识符
        int number = ConvertM2T(str, 16);
        printf("%d\n",number);
    }
}
笔记
  1. 16进制转换为10进制,输入的是字符串,所以要有一个函数用来char转int,注意ascii的计算
  2. 10进制转换为n进制是number先%n后/n,先得到低位再得到高位;而n进制转换为10进制是先得到高位n再加上低位n
  3. 输入字符串时若有多余,可以substr求子串排除掉多余的字符

例题6.4 进制转换2(清华大学复试上机)

题目

本题为m进制转换为n进制,可以先从m进制转换为10进制,再从10进制转换为n进制
题目地址

题解
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int Char2Int(char from){
    if('0' <= from && from <= '9'){
        return from - '0';//字符-'0'后为对应的数字
    }
    else{
        return from - 'A' + 10; //输入时为大写字母,所以仍用'A'
    }
}

char Int2Char(long long from){
    if(from < 10){
        return from + '0';
    }
    else{
        return from - 10 + 'a'; //这里注意题目要求为输出时用小写字母
    }
}

//m进制转换为10进制
long long ConvertM2T(string mstr,int m){
    long long number = 0;   //所有number的类型都用longlong,不然会报错
    for(int i = 0;i<mstr.size();i++){
        number *= m;
        number += Char2Int(mstr[i]);
    }
    return number;
}

//10进制转换为n进制
vector<char> ConvertT2N(long long number,int n){
    vector<char> answer;  //这里要定义一个answer来返回vector数据
    if(number == 0){
        answer.push_back('0');
    }
    else{
        while(number != 0){
            answer.push_back(Int2Char(number%n));
            number /= n;
        }
    }
    return answer;
}


int main(){
    int m,n;
    while(scanf("%d%d",&m,&n)!=EOF){
        string mstr;
        cin>>mstr;  //cin已经略过了上一行留下的换行符
        //先m进制转10进制,再10进制转n进制,使用longlong是本题数值过大
        long long number = ConvertM2T(mstr, m);
        vector<char> answer = ConvertT2N(number,n);

        for(int i = answer.size()-1;i>=0;i--){
            printf("%c",answer[i]);
        }
        printf("\n");
    }
}
笔记
  1. m进制-10进制-n进制,注意4个函数的写法,和数值类型
  2. 注意细节:题目要求的字母大小写、避免数值超范围用long long

2 最大公约数和最小公倍数

例题6.5 最大公约数(哈工大复试上机)

题目

题目地址

题解
#include <cstdio>

using namespace std;

int GCD(int a, int b){
    if(b == 0){
        return a;   //处理特殊情况
    }
    else{
        return GCD(b, a%b); //求最大公约数的公式
    }
}

int main(){
    int a, b;
    while(scanf("%d%d",&a,&b)!=EOF){
        printf("%d\n",GCD(a,b));
    }
    return 0;
}
笔记
  1. 求最大公约数的公式,欧几里得算法/ 辗转相除法,涉及递归调用

例题6.6 最小公倍数

最小公倍数的求解建立在最大公约数的基础上,就是个数学公式,
LCM(a,b) = (a*b) / GCD(a,b)

3 质数

包括:基础,给一个数判断是否为质数;区间内判断哪些是质数,也就是质数筛法

例题6.7 素数判定(哈工大复试上机)

题目

基础题,求素数
题目地址

题解
#include <iostream>
#include <cstdio>
#include <cmath>    //要用求根号的函数

using namespace std;

bool Judge(int n){
    if(n < 2){
        return false;   //0,1,负数都是非素数
    }
    int bound = sqrt(n);
    for(int i = 2;i<=bound;i++){    //注意这里可以=
        if(n%i == 0){
            return false;
        }
    }
    return true;
}

int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        if(Judge(n)){
            printf("yes\n");
        }
        else{
            printf("no\n");
        }
    }
}

笔记
  1. 添加头文件cmath库,使用sqrt求开根号
  2. 注意细节:0,1,负数都不是素数;判断的边界可以=bound=根号n

例题6.8 素数(北航复试上机)

题目

本题求第k个质数,用到了素数筛法,题目无地址

题解
#include <vector>
#include <cstdio>

using namespace std;

const int MAXN = 1e5+10;

vector<int> prime;	//保存质数
bool isPrime[MAXN];	//标记数组

void Initial(){
    fill(isPrime,isPrime+MAXN,true);    //fill的使用
    isPrime[0] = false;
    isPrime[1] = false;
    
    for(int i = 2;i<MAXN;++i){
        if(!isPrime[i]){
            continue;   //如果当前已经判断过不是质数,则跳过
        }
        prime.push_back(i);
        //健壮性(可不写)
        if(i < MAXN / i){
            continue;   //如果i*i过大,则不做处理,且判别式等价变换
        }
        //质数筛法,可以直接从i*i开始,避免重复计算,j+=i
        for(int j = i*i;j<MAXN;j+=i){
            isPrime[j] = false;
        }
    }
}

int main(){
    int k;
    while(scanf("%d",&k)!=EOF){
        printf("%d\n",prime[k-1]);
    }
}
笔记
  1. 用一个可动态增加的向量vector来记录素数,用const int MAXN来记录最大范围
  2. 记住质数筛法的写法,每次改isPrime的长度即可

4 分解质因数

先是求质因数,再根据质因数和约数个数的公式可以求约数的个数

例题6.9 质因数的个数(清华大学复试上机)

题目

本题求质因数的个数,用到了质数筛法,也用到了不太好记的统计质因数的流程
题目地址

题解
#include <cstdio>
#include <vector>

using namespace std;
//sqrt(1e9)将浮点数赋值给常整数需要高版本c++,所以可以自己口算后直接赋值整数
const int MAXN = 4e4;   

vector<int> prime;  //保存质数
bool isPrime[MAXN]; //标记数组

void Initial(){
    fill(isPrime,isPrime+MAXN,true);    //fill的使用
    isPrime[0] = false;
    isPrime[1] = false;
    
    for(int i = 2;i<MAXN;++i){
        if(!isPrime[i]){
            continue;   //如果当前已经判断过不是质数,则跳过
        }
        prime.push_back(i); //执行这一句时i已经必是质数了
        //健壮性
        if(i < MAXN / i){
            continue;   //如果i*i过大,则不做处理,且判别式等价变换
        }
        //质数筛法,可以直接从i*i开始,避免重复计算
        for(int j = i*i;j<MAXN;j+=i){
            isPrime[j] = false;
        }
    }
}

int main(){
    int answer = 0; //统计质因数个数,其中同一个素数有幂指数按多次算
    int n;
    Initial();
    while(scanf("%d",&n)!=EOF){
        for(int i = 0;i<prime.size() && prime[i]<n;i++){
            int factor = prime[i];
            //不断试除该素数,统计其幂指数
            while(n%factor == 0){
                n /= factor;
                answer ++;
            }
        }
        //遍历完所有小于MAXN的素数后,若n>1,则存在一个大于MAXN的因子且必为1次质因子
        if(n>1){
            answer++;
        }
        printf("%d\n",answer);
    }
}
笔记
  1. 质数筛法的流程
  2. 记住主函数中统计质因数的流程,不断试除、遍历后可能再+1
  3. 注意细节:const int MAXN赋值浮点数不稳妥,口算后赋值整数;主函数for的循环条件,以及别少写i++

习题 6.7 约数的个数(清华大学复试上机)

有个公式,约数的个数 = 所有质因数的(幂指数+1)相乘,比如12的质因数2,2,3,则约数个数为(2+1)*(1+1)= 6个

题目

题目地址

题解
#include <cstdio>
#include <vector>

using namespace std;
//sqrt(1e9)将浮点数赋值给常整数需要高版本c++,所以可以自己口算后直接赋值整数
const int MAXN = 4e4;

vector<int> prime;  //保存质数
bool isPrime[MAXN]; //标记数组

void Initial() {
    fill(isPrime, isPrime + MAXN, true); //fill的使用
    isPrime[0] = false;
    isPrime[1] = false;

    for (int i = 2; i < MAXN; ++i) {
        if (!isPrime[i]) {
            continue;   //如果当前已经判断过不是质数,则跳过
        }
        prime.push_back(i); //执行这一句时i已经必是质数了
        //健壮性
        if (i < MAXN / i) {
            continue;   //如果i*i过大,则不做处理,且判别式等价变换
        }
        //质数筛法,可以直接从i*i开始,避免重复计算
        for (int j = i * i; j < MAXN; j += i) {
            isPrime[j] = false;
        }
    }
}

int main() {
    
    int n;
    Initial();
    while (scanf("%d", &n) != EOF) {
        if (n == 0) {
            break;
        }
        for (int j = 0; j < n; j++) {   //每行有n个数据
            //统计约数的个数 = 每个质因数的(幂指数+1)再相乘,且初始化为1
            int answer =1; 
            
            int number;
            scanf("%d", &number);
            for (int i = 0; i < prime.size() && prime[i] < number; i++) {
                int exponent = 0;   //记录本次质因数的幂指数
                int factor = prime[i];
                //不断试除该素数,统计其幂指数
                while (number % factor == 0) {
                    number /= factor;
                    exponent++;
                }
                //每统计完一个质因数的幂指数,就累乘到约数个数answer上
                answer *= exponent + 1;
            }
            //遍历完所有小于MAXN的素数后,若n>1,则存在一个大于MAXN的因子且必为1次质因子
            if (number > 1) {
                //与上一道题区别是,这里直接把最后一个质因数的幂指数1+1,
                //乘到约数个数answer上即可
                answer *= 2;
            }
            printf("%d\n", answer);
        }
    }
}
笔记
  1. 与上一道例题的区别,需要根据题目修改输入输出格式,和对answer的初始化数值、初始化位置、和试除后的处理。
  2. 以前answer是初始化在主函数最开头,这道题多个输入,所以把answer初始化在每次输入后,且不是初始为0,而是1
  3. 以前answer是不断试除然后+1,并且最后再单独判断是否还有一个质因数;而这道题是用一个exponent代替原来的answer来记录每个质因数的幂指数
  4. 并且answer每次试除成功后answer *= exponent +1来记录约数的个数,最后如果还有1个质因数,则answer还要最后乘这个质因数的幂指数(1+1)

5 快速幂、矩阵、矩阵快速幂

快速幂的思想是:answer初始为1,将幂指数当二进制,这样就变成了原数“累乘所有二进制位的数值”,具体步骤记代码。

int answer = 1;
while(mi != 0){
	if(mi % 2 == 1){
		answer *= number;
		//answer %= mod;	//如果只需要求最后几位,可以在每次这个位置加上
	}
	mi /= 2;
	number *= number;	//底数不断平方,不管幂指数对应的二进制位当前是否为1
	//number %= mod;	//如果只需要求最后几位,可以每次在这里加上
}
return answer;

而矩阵求快速幂时的思想:套用快速幂的思想,不过answer初始为单位阵,较复杂,不过做题时现场能暴力写出来

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值