【算法笔记】第五章 数学问题

1. PAT B1019/A1069数字黑洞问题

代码为,需要注意的是数组定义大小为4但是在写sort函数时可以使用sort(a,a+4,cmp)这种表达方式,%04d可以补足4位,不足的用0补全:

#include<cstdio>
#include<algorithm>
using namespace std;

bool cmp(int a, int b)
{
    return a > b; //递减排序
}

//注意这种写法实现了对数组的实际改变,传入的应当相当于一个地址
void to_arr(int n, int num[])//从低位开始存储到数组当中
{
    for(int i = 0; i < 4; i++)
    {
        num[i] = n % 10;
        n /= 10;
    }
}

int to_number(int num[])
{
    int sum = 0;
    for(int i = 0; i < 4; i++)
    {
        sum = sum * 10 + num[i];
    }
    return sum;
}

int main()
{
    int n, MAX, MIN;
    scanf("%d", &n);
    int num[4];//注意这里将5改为4,因为num+4是最后一位的下一位
    while(1)
    {
        to_arr(n, num);
        sort(num, num + 4, cmp);
        MAX= to_number(num);
        sort(num, num + 4);
        MIN = to_number(num);
        n = MAX - MIN;
        printf("%04d - %04d = %04d\n", MAX, MIN, n);//实现当不足四位数时补0的操作:0123
        if(n == 0 || n == 6174)
            break;
    }
    return 0;
}

2.辗转相除法求最大公约数

#include<cstdio>
#include<algorithm>
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;
}

运行结果:

最小公倍数为 a * b / gcd(a, b) 

代码为:

#include<cstdio>
#include<algorithm>
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", a * b / gcd(a,b));
    }
    return 0;
}

运行结果:

 

3. 判断是否为素数,注意<math.h>中定义的sqrt中的参数必须是double型的,所以最好加上1.0*n,当然直接用n也不会出错,<= sqr,c++中允许使用true和false者两种bool类型的:

#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;

bool isPrime(int n)
{
    if(n <= 1)
        return false;
    int sqr = (int)sqrt(1.0 * n);
    for(int i = 2; i <= sqr; i++)
    {
        if(n % i == 0)
        return false;
    }
    return true;
}

int main()
{
    int m;
    while(scanf("%d", &m) != EOF)
    {
        if(isPrime(m))
            printf("%d is a prime!\n", m);
        else
            printf("%d is not a prime!\n", m);
    }
    return 0;
}

更简单的判断一段区间内的所有素数的方法,即从2开始,1不是素数,为2的倍数一定不是素数,3不是2的倍数(没有因为是前面的素数的倍数而被筛掉),一定是素数,按照这种思想依次类推,即可得到最终的结果:

#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;

const int maxn = 101;
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数

void find_prime()
{
    for(int i = 2; i < maxn; i++)
    {
        if(p[i] == false)
        {
            prime[pNum++] = i;
            for(int j = i + i; j < maxn; j += i)
            {
                p[j] = true;
            }
        }
    }
}

int main()
{
    find_prime();
    for(int i = 0; i < pNum; i++)
        printf("%d ", prime[i]);
    printf("\n");
    return 0;
}

结果:

 B1013整素数问题

输出从2开始的第m到第n个素数,第m个素数时prime[m-1],要注意对maxn进行修改,过小时无法通过:

#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;

int M,N;
const int maxn = 1000001;//对maxn进行修改
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数

void find_prime()
{
    for(int i = 2; i < maxn; i++)
    {
        if(p[i] == false)
        {
            prime[pNum++] = i;
            if(pNum >= N)
                break;
            for(int j = i + i; j < maxn; j += i)
            {
                p[j] = true;
            }
        }
    }
}

int main()
{
    int count = 0;
    scanf("%d%d", &M, &N);
    find_prime();
    for(int i = M - 1; i <= N - 1; i++)
    {
        printf("%d", prime[i]);
        count++;
        if(count % 10 != 0 && i < N - 1)
            printf(" ");
        else
            printf("\n");//当prime[i]的i为N-1时,要输出换行符
    }
    return 0;
}

结果:

4.质因子分解问题

质因子就是素数,所以还要用到前面的素数表,一个数的因子只可能有一个大于sqrt(n)的因子,所以或者所有因子都小于sqrt(n)或者有1个是大于sqrt(n)的,而有1个大于sqrt(n)的质因子,如14  = 2 * 7,17 = 17, 1不是素数,所以也不是质因子,不用在表达式中写出,按照这个思路可以得到各个质因子以及相同质因子的个数,n%prime[i],n/=prime[i]循环直到n%prime[i]不为0,代码为:

#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;

const int maxn = 1000001;//对maxn进行修改
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数

struct factor{
    int x;//具体的质因子
    int cnt = 0;//同一质因子的个数
}fac[10];

//可以获得全部的素数表prime[n]
void find_prime()
{
    for(int i = 2; i < maxn; i++)
    {
        if(p[i] == false)
        {
            prime[pNum++] = i;
            for(int j = i + i; j < maxn; j += i)
            {
                p[j] = true;
            }
        }
    }
}

int main()
{
    find_prime();
    int n, num = 0;//num是不同质因子的个数
    scanf("%d", &n);
    int N = n;
    int sqr = (int)sqrt(1.0 * n);
    if(n == 1)
        printf("1=1");//不用换行
    else
    {
        for(int i = 0; i < pNum && prime[i] <= sqr; i++)
        {
            if(n % prime[i] == 0)
            {
                fac[num].x = prime[i];
                //fac[num].cnt = 0;
                while(n % prime[i] == 0)
                {
                    fac[num].cnt++;
                    n /= prime[i];
                }
                num++;
            }
            if(n == 1)
                break;//1已经没有继续进行运算的必要了
        }
        if(n != 1)//说明有且有1个大于sqr的质因子,即为当前的n,可能为N自身,N为素数N=N,num=1
        {
            fac[num].x = n;
            fac[num].cnt = 1;
            num++;
        }
        printf("%d=", N);
        for(int i = 0; i < num; i++)
        {
            if(i > 0 )
                printf("*");
            printf("%d", fac[i].x);
            if(fac[i].cnt > 1)
                printf("^%d", fac[i].cnt);
        }
    }
    return 0;
}

运行结果:

5.大整数运算问题

对于一个结构体,不能在定义时初始化,即不可以struct ss{ int x = 0; int y = 0};这种形式,因为结构体在定义时是没有分配空间的,必须在声明时实例化,或者通过构造函数实现初始化!!!

加法

#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;

struct bign{
    int digit[1000];
    int len;
    bign(){
        memset(digit, 0, sizeof(digit));
        len = 0;
    }
};

bign change(char str[])
{
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++)
        a.digit[i] = str[a.len - 1 - i] - '0';
    return a;
}

bign add(bign a, bign b)
{
    bign c;
    int carry = 0;
    for(int i = 0; i < a.len || i < b.len; i++)//以最长的为界限
    {
        int temp = a.digit[i] + b.digit[i] + carry;
        c.digit[c.len++] = temp % 10;
        carry = temp / 10;//可能为0或者1,2等
    }
    //最高位,长度为len,但是len-1是最高位的下标
    if(carry != 0)
    {
        c.digit[c.len++] = carry;
    }
    return c;
}

void print(bign c)
{
    for(int i = c.len - 1; i >= 0; i--)
    {
        printf("%d", c.digit[i]);
    }
}

int main()
{
    char str1[1000], str2[1000];
    scanf("%s%s", str1, str2);
    bign a = change(str1);
    printf("%d\n", a.len);
    bign b = change(str2);
    printf("%d\n", b.len);
    bign c = add(a,b);
    printf("%d\n", c.len);
    print(c);
    return 0;
}

 运行结果:

减法,减法需要在低位不够时向高位借1,也是从低位开始运算,其它部分与上面的完全一致,只是sub函数不同,注意这个函数只能解决大的减去小的情况,如果小的减去大的,要先判断出大小,再进行大的减去小的并取负:

#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;

struct bign{
    int digit[1000];
    int len;
    bign(){
        memset(digit, 0, sizeof(digit));
        len = 0;
    }
};

bign change(char str[])
{
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++)
        a.digit[i] = str[a.len - 1 - i] - '0';
    return a;
}

bign sub(bign a, bign b)
{
    bign c;
    for(int i = 0; i < a.len || i < b.len; i++)
    {
        if(a.digit[i] < b.digit[i])
        {
            a.digit[i + 1]--;
            a.digit[i] += 10;
        }
        c.digit[c.len++] = a.digit[i] - b.digit[i];
    }
    //c.len - 1 == 0时只有1位,c.len == 1,c.len - 1是最高位的下标
    while(c.len - 1 >= 1 && c.digit[c.len -1] == 0)
    {
        c.len--;
    }
    return c;
}

void print(bign c)
{
    for(int i = c.len - 1; i >= 0; i--)
    {
        printf("%d", c.digit[i]);
    }
}

int main()
{
    char str1[1000], str2[1000];
    scanf("%s%s", str1, str2);
    bign a = change(str1);
    printf("%d\n", a.len);
    bign b = change(str2);
    printf("%d\n", b.len);
    bign c = sub(a,b);
    printf("%d\n", c.len);
    print(c);
    return 0;
}

 运行结果:

乘法,与加法比较类似,主要注意进位和最高位的问题,与一般乘法思路不同的是处理大整数和小整数的问题中,是用大整数的每一位去乘完整的小整数:

#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;

struct bign{
    int digit[1000];
    int len;
    bign(){
        memset(digit, 0, sizeof(digit));
        len = 0;
    }
};

bign change(char str[])
{
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++)
        a.digit[i] = str[a.len - 1 - i] - '0';
    return a;
}

bign mul(bign a, int b)
{
    bign c;
    int carry = 0;
    for(int i = 0; i < a.len; i++)
    {
        int temp = a.digit[i] * b + carry;
        c.digit[c.len++] = temp % 10;
        carry = temp / 10;
    }
    //与加法不同,乘法的进位可能会有多位,大于10,加法不能进位10
    while(carry != 0)
    {
        c.digit[c.len++] = carry % 10;
        carry /= 10;
    }
    return c;
}

void print(bign c)
{
    for(int i = c.len - 1; i >= 0; i--)
    {
        printf("%d", c.digit[i]);
    }
}

int main()
{
    char str1[1000];
    int b;
    //scanf以空格作为结束
    scanf("%s%d", str1, &b);
    bign a = change(str1);
    printf("a.len = %d\n", a.len);
    bign c = mul(a, b);
    printf("mul(a,b) = ");
    print(c);
    return 0;
}

运行结果:

 除法:除法比较特别,从高位开始计算,每次的余数*10加上下一位作为下一位的被除数:

#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;
int r = 0;

struct bign{
    int digit[1000];
    int len;
    bign(){
        memset(digit, 0, sizeof(digit));
        len = 0;
    }
};

bign change(char str[])
{
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++)
        a.digit[i] = str[a.len - 1 - i] - '0';
    return a;
}

bign divide(bign a, int b)
{
    bign c;
    c.len = a.len;//使得商与被除数的位数一样长
    for(int i = c.len - 1; i >= 0; i--)
    {
        r = r * 10 + a.digit[i];
        if(r < b)
            c.digit[i] = 0; //这里实际上应该可以直接用r/b如果r<b时,r/b=0
        else
        {
            c.digit[i] = r / b;
            r = r % b;
        }
    }
    while(c.len - 1 >= 1 && c.digit[c.len - 1] == 0)
    {
        c.len--;
    }
    return c;
}

void print(bign c)
{
    for(int i = c.len - 1; i >= 0; i--)
    {
        printf("%d", c.digit[i]);
    }
}

int main()
{
    char str1[1000];
    int b;
    //scanf以空格作为结束
    scanf("%s%d", str1, &b);
    bign a = change(str1);
    printf("a.len = %d\n", a.len);
    bign c = divide(a, b);
    printf("divide(a,b) = ");
    print(c);
    return 0;
}

运行结果:

借此解决一下王道上给出的问题N的阶乘,N<=1000,每个数都可以作为小整数,而每次的结果都可以作为大整数,先将"1"转换为bign,依次为基础逐渐与各个整数相乘可以得到结果,代码为,只是将主函数上做了改变,其它的地方都未作出改变:

#include<cstdio>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;

struct bign{
    int digit[1000];
    int len;
    bign(){
        memset(digit, 0, sizeof(digit));
        len = 0;
    }
};

bign change(char str[])
{
    bign a;
    a.len = strlen(str);
    for(int i = 0; i < a.len; i++)
    {
        a.digit[i] = str[a.len - 1 - i] - '0';
    }
    return a;
}

bign mul(bign a, int b)
{
    bign c;
    int carry = 0;
    for(int i = 0; i < a.len; i++)
    {
        int temp = a.digit[i] * b + carry;
        c.digit[c.len++] = temp % 10;
        carry = temp / 10;
    }
    while(carry != 0)
    {
        c.digit[c.len++] = carry % 10;
        carry /= 10;
    }
    return c;
}

void print(bign c)
{
    for(int i = c.len - 1; i >= 0; i--)
    {
        printf("%d", c.digit[i]);
    }
}



int main()
{
    int N;
    char str[100] = "1";
    while(scanf("%d", &N) != EOF)
    {
        bign res = change(str);
        for(int i = 2; i <= N; i++)
        {
            res = mul(res, i);
        }
        print(res);
    }
    return 0;
}

运行结果:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值