whenever

  • Home

  • Tags21

  • Categories6

  • Archives122

  • About

PAT乙级1034 || 有理数四则运算(详解,C/C++示例,测试点分析)

Posted on 2019-09-30 In PAT

有理数四则运算

题目描述

本题要求编写程序,计算 2 个有理数的和、差、积、商。

输入格式

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

输出格式

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

输入样例1

1
2/3 -4/2

输出样例1

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

输入样例2

1
5/3 0/6

输出样例2

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

问题解决

解题思想

这道题是我刷PAT乙级以来最费时间的一次了,前前后后产生了好多细节错误。这里给出的示例代码也不简洁,但仍然给出的原因是这样较冗长一些的代码可能更便于我们理解题目的解决过程。

解决本题的要点是用结构体类型来存储有理数的各要素,对每一次输出的操作数或结果数都要进行相应的化简,包括分子分母要约分为最简;计算时要用分式进行(整数部分要转化到分式中),+与-运算前还需要通分。

输出格式的控制涉及很多的细节,比如:

  • 负数加括号
  • 分子分母能约分的要约分(这就要求分子分母的最大公约数)
  • 分母为0时直接输出Inf

具体见下方示例代码。其次进行除运算时,结果有理数的分母可能为负,要将其转移到分子上,以便后续统一处理。

坑点提醒

  1. 有理数的分子分母及整数部分要定义为long long型,除法或乘法运算可能会产生很大的中间数。
  2. 求最大公约数时不要使用穷举法,这样会导致测试点2与测试点3超时,应使用辗转相除法,这也是看了许多大佬的博客才得知的。

代码示例(C/C++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <cstdio>
#include <cmath>
using namespace std;
//声明有理数的结构体类型
struct rational
{
long long integer;//整数部分,初始化为0
long long numerator;//分子
long long denominator;//分母
}rat1,rat2;
char c[4] = {'+','-','*','/'};
void Simplify_Print_Rat(rational rat);
rational Rfcd_Operate_Rat(rational rat1,rational rat2,int i);
int main()
{
scanf("%lld/%lld",&rat1.numerator,&rat1.denominator);//输入第一个有理数
scanf("%lld/%lld",&rat2.numerator,&rat2.denominator);//输入第二个有理数
rat1.integer = rat2.integer = 0;//有理数的整数部分初始化为0
rational result_rat;
for(int i = 0; i < 4; i++){
Simplify_Print_Rat(rat1);
printf(" %c ",c[i]);
Simplify_Print_Rat(rat2);
printf(" = ");
result_rat = Rfcd_Operate_Rat(rat1,rat2,i);
Simplify_Print_Rat(result_rat);
printf("\n");
}
return 0;
}
//化简并输出有理数
void Simplify_Print_Rat(rational rat)
{
if(rat.denominator != 0){//分母不为0时化简才有意义
rat.integer += rat.numerator / rat.denominator;
rat.numerator %= rat.denominator;
}
if(rat.numerator != 0){//分子不为0时约分分式才有必要,否则没必要
long long maxdivisor = rat.numerator;//分子分母的最大公约数
long long a = rat.denominator;
long long b = rat.numerator;
for(long long i = a % b; i != 0; i = a % b){//求a与b的最大公约数
a = b;
b = i;
}
maxdivisor = fabs(b);//注意此处要加绝对值符号,否则会影响下面的符号
rat.numerator /= maxdivisor;
rat.denominator /= maxdivisor;
}
if((rat.numerator < 0||rat.integer < 0)&&
rat.denominator != 0){//分子或整数部分小于0且分母不为0时输出需要加括号
printf("(");
}
if(rat.integer != 0){//整数部分不为0时需要输出
if(rat.numerator != 0){//分子不为0时输出整数后还要输出分式故还需要空格
printf("%lld ",rat.integer);
}
else{//分子为0时只需输出整数
printf("%lld",rat.integer);
}
}
if(rat.numerator == 0){
if(rat.integer == 0){
printf("0");//分子和整数部分均为0,输出一个0即可
}
}
else{
if(rat.denominator == 0){//分母为0需要输出Inf
printf("Inf");
}
else{
if(rat.integer < 0){//整数部分小于0则分子必小于0(不为0时)此时分子负号不需输出
rat.numerator = fabs(rat.numerator);
}
printf("%lld/%lld",rat.numerator,rat.denominator);
}
}
if((rat.numerator < 0||rat.integer < 0)&&
rat.denominator != 0){
printf(")");
}
}
//通分并计算有理数
rational Rfcd_Operate_Rat(rational rat1,rational rat2,int i)
{
rational rat3;
rat3.integer = 0;
if(i == 2||i == 3){
switch(i)
{
case 2: //乘
rat3.numerator = rat1.numerator * rat2.numerator;
rat3.denominator = rat1.denominator * rat2.denominator;
break;
default: //除
rat3.denominator = rat1.denominator * rat2.numerator;
rat3.numerator = rat1.numerator * rat2.denominator;
}
}
else{
rat1.numerator *= rat2.denominator;//这四行为通分过程
rat2.numerator *= rat1.denominator;
rat1.denominator *= rat2.denominator;
rat2.denominator = rat1.denominator;
switch(i)
{
case 0: //加
rat3.numerator = rat1.numerator + rat2.numerator;
rat3.denominator = rat1.denominator;
break;
default: //减
rat3.numerator = rat1.numerator - rat2.numerator;
rat3.denominator = rat1.denominator;
break;
}
}
if(rat3.denominator < 0){//除法可能会将负号移至分母处
rat3.denominator = -rat3.denominator;
rat3.numerator = - rat3.numerator;
}
return rat3;
}

题目来源:PAT乙级1034
作者:CHEN, Yue
单位:浙江大学

稀罕作者
Mengzhao Wang WeChat Pay

WeChat Pay

Mengzhao Wang Alipay

Alipay

# C/C++ # PAT # 编程
PAT乙级1033 || 旧键盘打字(详解,C/C++示例,测试点分析)
PAT乙级1035 || 插入与归并(详解,C/C++示例,测试点分析)
  • Table of Contents
  • Overview
Mengzhao Wang

Mengzhao Wang

Try? All the way !
122 posts
6 categories
21 tags
  1. 1. 有理数四则运算
    1. 1.1. 题目描述
    2. 1.2. 输入格式
    3. 1.3. 输出格式
    4. 1.4. 输入样例1
    5. 1.5. 输出样例1
    6. 1.6. 输入样例2
    7. 1.7. 输出样例2
    8. 1.8. 问题解决
      1. 1.8.1. 解题思想
      2. 1.8.2. 坑点提醒
      3. 1.8.3. 代码示例(C/C++)
© 2021 Mengzhao Wang