作者的话:若有朋友复制代码去PAT试着运行遇到问题的:
1.可能是格式问题,可以先把从本站复制的代码粘贴到记事本,再把记事本里的代码复制,然后粘贴到PAT的代码区,提交本题回答,应该就可以了;
2.可能是注释原因,PAT有时候检测到注释会编译错误,所以可以先把注释删了,再进行提交回答;
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
二、解题思路
读题:
输入两个分数形式的有理数,要求程序分四行以最简形式输出两个有理数的加减乘除四个算式。
最简形式:k a/b(其中k是整数部分,a/b是最简分数部分;若为负数,则需加括号;若除法分母为0,则输出Inf;若k为非0,a以正数形式显示输出)。
思路:
1 变量定义:因为输出时都需要转成最简形式(k a/b),所以两个输入的有理数都各需要三个变量来存储k、a、b三个部分,而运算结果的k、a、b三部分我将放在运算函数里定义,故不在此定义;
2 接收两个分数;
3 形式变换:通过调用函数fraction(),对输入的两个分数进行约分,并变成最简形式;
4 对四则运算的前半部分输出:k或a为负数的有理数,用括号括起(对单个有理数的输出要遵循题目规定的格式,所以我定义了单个有理数的格式输出函数myprintf());
5 四则运算计算功能的实现。(函数调用在第四步①的代码中)
三、具体实现
0.标准C源程序框架
#include <stdio.h>
int main()
{
return 0;
}
1 变量定义:因为输出时都需要转成最简形式(k a/b),所以两个输入的有理数都各需要三个变量来存储k、a、b三个部分,而运算结果的k、a、b三部分我将放在运算函数里定义,故不在此定义;
long long k1 = 0, a1, b1;//三个变量用于存储输入的第一个有理数的最简形式的三个部分
long long k2 = 0, a2, b2;//用于存储输入的第二个有理数的最简形式的三个部分
2 接收两个分数;
scanf("%lld/%lld %lld/%lld", &a1, &b1, &a2, &b2);
3 形式变换:通过调用函数fraction(),对输入的两个分数进行约分,并变成最简形式;
①形式变换函数调用部分
fraction(&k1, &a1, &b1);
fraction(&k2, &a2, &b2);
②形式变换函数定义部分
void fraction(long long* pk, long long* pa, long long* pb)//分数形式,小数形式
{
//转化成最简形式
*pk = (*pa) / (*pb);
(*pa) = (*pa) - (*pk) * (*pb);
if ((*pk) != 0)
(*pa) = (*pa) > 0 ? (*pa) : (-*pa);
//约分 - 经过最简形式转化后,此时a<b
long long r = reduc((*pa), (*pb));
(*pa) = (*pa) / r;
(*pb) = (*pb) / r;
}
③约分函数定义部分(测试点3、4对程序运行效率有较高要求,所以要尽量优化约分函数,要求熟练掌握辗转相除法)
long long reduc(long long a, long long b)
{
long long temp;
while (b != 0) {
temp = a % b;
a = b;
b = temp;
}
return a>0?a:(-a);
}
4 对四则运算的前半部分输出:k或a为负数的有理数,用括号括起(对单个有理数的输出要遵循题目规定的格式,所以我定义了单个有理数的格式输出函数myprintf());
①实现四则运算前半部分的输出
//四则运算
//加法
myprintf(k1, a1, b1);//第一个有理数的输出
printf(" + ");
myprintf(k2, a2, b2);//第二个有理数的输出
printf(" = ");
add(k1, a1, b1, k2, a2, b2);//加法运算的实现
printf("\n");
//减法
myprintf(k1, a1, b1);
printf(" - ");
myprintf(k2, a2, b2);
printf(" = ");
sub(k1, a1, b1, k2, a2, b2);//减法运算的实现
printf("\n");
//乘法
myprintf(k1, a1, b1);
printf(" * ");
myprintf(k2, a2, b2);
printf(" = ");
mul(k1, a1, b1, k2, a2, b2);//乘法运算的实现
printf("\n");
//除法
myprintf(k1, a1, b1);
printf(" / ");
myprintf(k2, a2, b2);
printf(" = ");
div(k1, a1, b1, k2, a2, b2);//除法运算的实现
②函数myprintf()的定义
void myprintf(long long k, long long a, long long b)//符合本题条件的特殊格式打印
{
if (k < 0 || a < 0)//k或a有一个为0即说明当前有理数为负数,所以需要加括号,此处先输出左括号
printf("(");
if (k != 0)//如果k不为0,则需要输出k
printf("%lld", k);
if (k && a) printf(" ");//如果k和a都非0,即需要显示,则两者之间还需要输出一个空格
if (a != 0)//如果a不为0,则需要输出a/b
printf("%lld/%lld", a, b);
if (k < 0 || a < 0)//k或a有一个为0即说明当前有理数为负数,所以需要加括号,此处输出右括号
printf(")");
if (k == 0 && a == 0)//如果k、a均为0,即当前有理数为0,输出0
printf("0");
}
5 四则运算计算功能的实现。(函数调用在第四步①的代码中)
//加法
void add(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 + k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
//当k非0时最简形式的a必显示为正数,但实际可能是负数,不在运算前改回来的话可能会导致结果错误
//我就因为忽略了这个点在测试点3卡了很久,所以务必抓好每个细节
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
//加法的话可以整数部分和整数部分相加,分数部分相加,再把整数部分和分数部分结合成一个分数,
//后用形式变换函数fraction()进行约分和转变为最简形式,最后将结果进行打印
k = k1 + k2;
if (k != 0)
a = a1 * b2 + b1 * a2 + k * b1 * b2;
else
a = a1 * b2 + b1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//减法
void sub(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 - k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
k = k1 - k2;
if (k != 0)
a = a1 * b2 - b1 * a2 + k * b1 * b2;
else
a = a1 * b2 - b1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//乘法
void mul(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 * k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
a1 = a1 + k1 * b1;
a2 = a2 + k2 * b2;
a = a1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//除法
void div(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 / k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
a1 = a1 + k1 * b1;
a2 = a2 + k2 * b2;
if (a2 == 0) printf("Inf");
else//(a1*b2)/(b1*a2)
{
a = a1 * b2;
b = b1 * a2;
if (b < 0)
{
a = -a;
b = -b;
}
fraction(&k, &a, &b);
myprintf(k, a, b);
}
}
四、测试数据
1 题目所给输入、输出样例多用用,给自己的代码进行测试(样例过了,测试点1、2应该都过了);
2 题目给出的整数可能超过int范围,所以整数都必须用long long型存放,不然最后两个测试点过不去。(虽然题目有说结果不会超出int,但因为我们的算法都是先计算结果再约分,所以可能超出;出题者希望我们的算法应该是和平常做分数乘法时一样,先对乘法两边的分子分母交叉进行约分,再相乘得到最终结果,这样应该可以使用int完成本题代码。奈何本人懒惰,选择挥霍空间完成这道题,大家凑合凑合)(对应测试点3、4);
3 测试点3、4对程序运行效率有较高要求,所以要尽量优化约分函数,要求熟练掌握辗转相除法;
4 测试点3过不去的也可以考虑考虑分子分母同为负数的情况;
5 测试点3过不去也可以试一下输入-5/2 1/2,看能否得到正确结果;(这个最重要!!!)
(哪个测试点折磨我最久大家应该一目了然了,crying)
五、初版代码(失败版本,因为思路不够清晰,程序也没有进行模块划分,代码显得杂乱,测试点3、4一直失败,调试无果后我选择放弃这个版本)
/*目标:输入两个分数形式的有理数,要求程序分四行输出两个有理数最简形式的加减乘除四个算式.
最简形式:k a/b(其中k是整数部分,a/b是最简分数部分;若为负数,则需加括号;若除法分母为0,则输出Inf).
*/ // a1/b1 a2/b2
/*步骤分解:
1 变量预备。因为程序的输入是两个分数形式的有理数,每个分数由分子、'/'、分母组成(分子、分母均为整数)。
所以两个分数形式的有理数总共有四个整数需要存储,故需要定义四个整型变量,而'/'没有太多意义,不需要进行存储。
而四则运算的结果要以最简形式输出,形式为k a/b,所以有必要再定义三个变量存储运算结果。
2 输入接收。
3 四则运算。根据输入实现四则运算,计算出结果。
4 输出显示*/
#include <stdio.h>
void fraction(int k, int a, int b)
{
int t = a > 0 ? a : (-a);
for (int i = t; i >= 2; i--)
{
if (a % i == 0 && b % i == 0)
{
a = a / i;
b = b / i;
break;
}
}
if (k < 0)
{
printf("(%d", k);
if (a > 0)
printf(" %d/%d)", a, b);
else
printf(")");
}
else if (k > 0)
{
printf("%d",k);
if (a > 0)
printf(" %d/%d", a, b);
}
else //k==0
{
if (a > 0) printf("%d/%d",a,b);
else if (a < 0) printf("(%d/%d)",a,b);
else printf("0");
}
}
int main()
{
int k1 = 0, a1, b1;
int k2 = 0, a2, b2;
int k, a, b;
scanf("%d/%d %d/%d",&a1,&b1,&a2,&b2);
k1 = a1 / b1;
k2 = a2 / b2;
//加法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" + ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * b2 + a2 * b1) / (b1 * b2);
if (k < 0) a = -((a1 * b2 + a2 * b1) - k * (b1 * b2));
else a = (a1 * b2 + a2 * b1) - k * (b1 * b2);
b = (b1 * b2);
fraction(k,a,b);
printf("\n");
//减法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" - ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * b2 - a2 * b1) / (b1 * b2);
if (k < 0) a = -((a1 * b2 - a2 * b1) - k * (b1 * b2));
else a = (a1 * b2 - a2 * b1) - k * (b1 * b2);
b = (b1 * b2);
fraction(k, a, b);
printf("\n");
//乘法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" * ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * a2) / (b1 * b2);
if (k < 0) a = -(a1 * a2 - k * b1 * b2);
else a = a1 * a2 - k * b1 * b2;
b = b1 * b2;
fraction(k, a, b);
printf("\n");
//除法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" / ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
if (k2 == 0 && (a2 - k2 * b2) == 0) printf("Inf");
else
{ // 3/-2 -3/-2 -2/3
// k -1 a 1 b 2
// k 1 a 1 b 2
// k 0 a -2 b 3
k = a1 * b2 / (b1 * a2);
if (k == 0)
{
if (a2 < 0)
{
a1 = -a1;
a2 = -a2;
}
a = a1 * b2;
}
else if (k > 0)
{
if (a2 < 0)
{
a1 = -a1;
a2 = -a2;
}
a = a1 * b2 - k * b1 * a2;
}
else
{
if (a2 < 0)
{
a1 = -a1;
a2 = -a2;
}
a = -(a1 * b2 - k * b1 * a2);
}
b = b1 * a2;
fraction(k, a, b);
}
return 0;
}
六、第二版代码(做了更多的模块划分,程序思路较初版清晰许多,已通过测试)
/*目标:输入两个分数形式的有理数,要求程序分四行输出两个有理数最简形式的加减乘除四个算式.
最简形式:k a/b(其中k是整数部分,a/b是最简分数部分;若为负数,则需加括号;
若除法分母为0,则输出Inf).
*/ // a1/b1 a2/b2
/*
步骤分解:
1 变量定义:因为输出时都需要转成最简形式(k a/b),所以两个输入的有理数以及最后的运算结果都各需要
三个变量来存储k、a、b三个部分;
2 接收两个分数;
3 形式变换:通过调用函数fraction(),对输入的两个分数进行约分,并变成最简形式;
4 对四则运算的前半部分输出:k为负数的有理数,用括号括起
5 四则运算计算功能的实现;
*/
#include <stdio.h>
long long reduc(long long a, long long b)
{
long long temp;
while (b != 0) {
temp = a % b;
a = b;
b = temp;
}
return a>0?a:(-a);
}
void fraction(long long* pk, long long* pa, long long* pb)//分数形式,小数形式
{
*pk = (*pa) / (*pb);
(*pa) = (*pa) - (*pk) * (*pb);
if ((*pk) != 0)
(*pa) = (*pa) > 0 ? (*pa) : (-*pa);
//约分 - 经过最简形式转化后,此时a<b
long long r = reduc((*pa), (*pb));
(*pa) = (*pa) / r;
(*pb) = (*pb) / r;
}
void myprintf(long long k, long long a, long long b)//符合本题条件的特殊格式打印
{
if (k < 0 || a < 0)//k或a有一个为0即说明当前有理数为负数,所以需要加括号,此处先输出左括号
printf("(");
if (k != 0)//如果k不为0,则需要输出k
printf("%lld", k);
if (k && a) printf(" ");//如果k和a都非0,即需要显示,则两者之间还需要输出一个空格
if (a != 0)//如果a不为0,则需要输出a/b
printf("%lld/%lld", a, b);
if (k < 0 || a < 0)//k或a有一个为0即说明当前有理数为负数,所以需要加括号,此处输出右括号
printf(")");
if (k == 0 && a == 0)//如果k、a均为0,即当前有理数为0,输出0
printf("0");
}
//加法
void add(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 + k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
//当k非0时最简形式的a必显示为正数,但实际可能是负数,不在运算前改回来的话可能会导致结果错误
//我就因为忽略了这个点在测试点3卡了很久,所以务必抓好每个细节
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
//加法的话可以整数部分和整数部分相加,分数部分相加,再把整数部分和分数部分结合成一个分数,
//后用形式变换函数fraction()进行约分和转变为最简形式,最后将结果进行打印
k = k1 + k2;
if (k != 0)
a = a1 * b2 + b1 * a2 + k * b1 * b2;
else
a = a1 * b2 + b1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//减法
void sub(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 - k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
k = k1 - k2;
if (k != 0)
a = a1 * b2 - b1 * a2 + k * b1 * b2;
else
a = a1 * b2 - b1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//乘法
void mul(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 * k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
a1 = a1 + k1 * b1;
a2 = a2 + k2 * b2;
a = a1 * a2;
b = b1 * b2;
fraction(&k, &a, &b);
myprintf(k, a, b);
}
//除法
void div(long long k1, long long a1, long long b1, long long k2, long long a2, long long b2)
{ //k1 a1/b1 / k2 a2/b2
long long k, a, b;//用于四则运算计算结果的最简形式的三个部分
if (k1 < 0)a1 = -a1;
if (k2 < 0)a2 = -a2;
a1 = a1 + k1 * b1;
a2 = a2 + k2 * b2;
if (a2 == 0) printf("Inf");
else//(a1*b2)/(b1*a2)
{
a = a1 * b2;
b = b1 * a2;
if (b < 0)
{
a = -a;
b = -b;
}
fraction(&k, &a, &b);
myprintf(k, a, b);
}
}
int main()
{
long long k1 = 0, a1, b1;//三个变量用于存储输入的第一个有理数的最简形式的三个部分
long long k2 = 0, a2, b2;//用于存储输入的第二个有理数的最简形式的三个部分
scanf("%lld/%lld %lld/%lld", &a1, &b1, &a2, &b2);
fraction(&k1, &a1, &b1);
fraction(&k2, &a2, &b2);
//四则运算
//加法
myprintf(k1, a1, b1);//第一个有理数的输出
printf(" + ");
myprintf(k2, a2, b2);//第二个有理数的输出
printf(" = ");
add(k1, a1, b1, k2, a2, b2);//加法运算的实现
printf("\n");
//减法
myprintf(k1, a1, b1);
printf(" - ");
myprintf(k2, a2, b2);
printf(" = ");
sub(k1, a1, b1, k2, a2, b2);//减法运算的实现
printf("\n");
//乘法
myprintf(k1, a1, b1);
printf(" * ");
myprintf(k2, a2, b2);
printf(" = ");
mul(k1, a1, b1, k2, a2, b2);//乘法运算的实现
printf("\n");
//除法
myprintf(k1, a1, b1);
printf(" / ");
myprintf(k2, a2, b2);
printf(" = ");
div(k1, a1, b1, k2, a2, b2);//除法运算的实现
return 0;
}
七、初版代码修正(经过第二版代码理清思路后,把初版代码的问题找出来并进行修正,最后通过测试)
/*目标:输入两个分数形式的有理数,要求程序分四行输出两个有理数最简形式的加减乘除四个算式.
最简形式:k a/b(其中k是整数部分,a/b是最简分数部分;若为负数,则需加括号;若除法分母为0,则输出Inf).
*/ // a1/b1 a2/b2
/*步骤分解:
1 变量预备。因为程序的输入是两个分数形式的有理数,每个分数由分子、'/'、分母组成(分子、分母均为整数)。
所以两个分数形式的有理数总共有四个整数需要存储,故需要定义四个整型变量,而'/'没有太多意义,不需要进行存储。
而四则运算的结果要以最简形式输出,形式为k a/b,所以有必要再定义三个变量存储运算结果。
2 输入接收。
3 四则运算。根据输入实现四则运算,计算出结果。
4 输出显示*/
#include <stdio.h>
void fraction(long long k, long long a, long long b)
{
long long temp;
long long a1 = a, b1 = b;
while (b1 != 0) {
temp = a1 % b1;
a1 = b1;
b1 = temp;
}
a1 = a1 > 0 ? a1 : (-a1);
a = a / a1;
b = b / a1;
if (k < 0)
{
printf("(%lld", k);
a = -a;
if (a > 0)
printf(" %lld/%lld)", a, b);
else printf(")");
}
else if (k > 0)
{
printf("%lld", k);
if (a > 0)
printf(" %lld/%lld", a, b);
}
else //k==0
{
if (a > 0) printf("%lld/%lld", a, b);
else if (a < 0) printf("(%lld/%lld)", a, b);
else printf("0");
}
}
int main()
{
long long k1 = 0, a1, b1;
long long k2 = 0, a2, b2;
long long k, a, b;
scanf("%lld/%lld %lld/%lld", &a1, &b1, &a2, &b2);
k1 = a1 / b1;
k2 = a2 / b2;
//加法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" + ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * b2 + a2 * b1) / (b1 * b2);
a = (a1 * b2 + a2 * b1) - k * (b1 * b2);
b = (b1 * b2);
fraction(k, a, b);
printf("\n");
//减法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" - ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * b2 - a2 * b1) / (b1 * b2);
a = (a1 * b2 - a2 * b1) - k * (b1 * b2);
b = (b1 * b2);
fraction(k, a, b);
printf("\n");
//乘法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" * ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
k = (a1 * a2) / (b1 * b2);
a = a1 * a2 - k * b1 * b2;
b = b1 * b2;
fraction(k, a, b);
printf("\n");
//除法运算
fraction(k1, a1 - k1 * b1, b1);
printf(" / ");
fraction(k2, a2 - k2 * b2, b2);
printf(" = ");
if (a2 == 0) printf("Inf");
else
{
k = a1 * b2 / (b1 * a2);
if (a2 < 0)
{
a1 = -a1;
a2 = -a2;
}
a = a1 * b2 - k * b1 * a2;
b = b1 * a2;
fraction(k, a, b);
}
return 0;
}