PAT乙级1034

270 阅读3分钟

题目

本题要求编写程序,计算 2 个有理数的和、差、积、商。(最难的题,往往有最简单的描述

输入格式

输入在一行中按照 a1/b1 a2/b2 的格式给出两个分数形式的有理数,其中分子和分母全是整型范围内的整数,负号只可能出现在分子前,分母不为 0。

输出格式

分别在 4 行中按照 有理数1 运算符 有理数2 = 结果 的格式顺序输出 2 个有理数的和、差、积、商。注意输出的每个有理数必须是该有理数的最简形式 k a/b,其中 k 是整数部分,a/b 是最简分数部分;若为负数,则须加括号;若除法分母为 0,则输出 Inf。题目保证正确的输出中没有超过整型范围的整数。

样例

输入样例1:

2/3 -4/2

输出样例1:

2/3 + (-2) = (-1 1/3)
2/3 - (-2) = 2 2/3
2/3 * (-2) = (-1 1/3)
2/3 / (-2) = (-1/3)

输入样例2:

5/3 0/6

输出样例2:

1 2/3 + 0 = 1 2/3
1 2/3 - 0 = 1 2/3
1 2/3 * 0 = 0
1 2/3 / 0 = Inf

思路

gcd函数

欧几里得算法,也就是高中数学书上的辗转相除法。他的核心是:gcd(a,b) == gcd(b,a % b) ,而终止递归的条件就是b(除数)变为了0,这个时候已经返回了a,b的最大公约数。

//gcd算法
int gcd(int a,int b) {
    if(b == 0)
        return a;
    return gcd(b,a % b);
}

还有个简版:

long long gcd(long long a,long long b) {return b == 0 ? a : gcd(b,a % b);}

注意点

  1. 负数要有括号,为了判断是否为负数,用一个flag保存是否为负数,是de话就要输入括号与负号。
  2. 假分数时的整数部分若为0不输出,但分子分母能整除时只输出整数部分
  3. 分母为0时输出Inf,分子为0时输出0.记住一旦为0要return 直接跳出,不进行接下来的操作。(看了好久
  4. 输入的记住用long long,数据有点大(a1*b1时可能爆掉)
  5. 注意空格

代码:

gcd函数寻找最大公约数,workout函数进行格式化输出。

//foreverking
#include <vector>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;

//分子和分母全是整型范围内的整数,负号只可能出现在分子前,分母不为 0
long long a1,a2,b1,b2;

//gcd,辗转相除求最大公约数
long long gcd(long long a,long long b) {
    //return b == 0 ? a : gcd(b,a % b);
    if(b == 0)
        return a;
    return gcd(b,a % b);
}

//输出处理过后的数字
void workOut(long long m,long long n) {
    //判断有0没
    if(m * n == 0) {
        if(m == 0) cout << 0;
        if(n == 0) cout << "Inf";
        return;
    }
    //这是一个负数,注意负号要在分子,而且负号有括号
    bool flag = 0;
    if((m < 0 && n > 0) || (m > 0 && n < 0))
        flag = 1;;//是负数则flag为1
    m = abs(m);
    n = abs(n);//都取绝对值
    long long num = m / n;//如果是带分数,那么这是整数部分
    long long x = gcd(m,n);//最大公约数
    m /= x;
    n /= x;//得出约分后的分子分母
    m = m - num * n;
    if(flag == 1) cout << "(-";//实际负数,输出括号与负号
    if(num != 0) cout << num;//有整数部分,输出
    //可以整除,没有分数部分了,是负数就输出括号,不是直接返回了
    if(m % n== 0){
        if(flag == 1) cout << ')';
        return;
    }
    if(num != 0) cout << ' ';
    cout << m << '/' << n;
    if(flag == 1) cout << ')';
}


int main() {
    //a1/b1 a2/b2
    scanf("%lld/%lld %lld/%lld",&a1,&b1,&a2,&b2);
   	//加法
	workOut(a1,b1);
    cout << " + ";
    workOut(a2,b2);
    cout << " = ";
    workOut(a1 * b2 + a2 * b1, b1 * b2);
    cout << endl;
    //减法
	workOut(a1, b1);
	cout << " - ";
	workOut(a2, b2);
    cout << " = ";
	workOut(a1 * b2 - a2 * b1,b1 * b2);
	cout << endl;
	//乘法
	workOut(a1, b1);
	cout << " * ";
	workOut(a2, b2);
	cout << " = ";
	workOut(a1 * a2,b1 * b2);
	cout << endl;
	//除法
	workOut(a1,b1);
    cout << " / ";
    workOut(a2,b2);
    cout << " = ";
    workOut(a1 * b2,b1 * a2);
	cout << endl;
	return 0;
}

有一些语句是可以优化的。 题目链接