目录

​1,题目描述​

​ 题目描述​

​2,思路​

​注意​

​算法​

​3,AC代码​

​4,解题过程h​

​第一搏​

​辗转相除法​

​第二搏​

​第三搏​


1,题目描述

  • rational:合理的; 理性的; 明智的; 理智的; 清醒的;
  • numerator:(分数中的) 分子;
  • denominator:分母;

PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_PAT

Sample Input 1:

5
2/5 4/15 1/30 -2/60 8/3

 

Sample Output 1:

3 1/3

 

Sample Input 2:

2
4/3 2/3

 

Sample Output 2:

2

 

Sample Input 3:

3
1/3 -1/6 1/8

 

Sample Output 3:

7/24

 题目描述

将给出的所有分数求和,并化简为最简形式。

 

2,思路

注意

  • 由于数据比较大,统一通分之后再化简可能会溢出,需要加一次化简一次;
  • 每次读入的分数可能不是最简形式的,需要先化简,尽可能地避免溢出;
  • 最小公倍数 =x * y / 最大公因数
  • 递归求最大公因数时,要输出绝对值

算法

  1. 设计函数gcd,求任意两数的最大公因数:
  2. PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_1081_02

  3. 每接收一个分数,将其化简为最简形式,并与以前的累加和相加,在对累加和进行化简:
  4. PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_甲级_03

  5. 分情况对最终结果进行输出:
  6. PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_PAT_04

 

3,AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b){ //辗转相除法求最大公约数
return b == 0 ? abs(a) : gcd(b, a % b); //注意取绝对值 无论a与b的大小 第一次递归后总能将较大值放在参数1的位置
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
ll N, a, b, suma = 0, sumb = 1, maxFactor; //suma累加后的分子 sumb累加后的分母
scanf("%lld", &N);
for(int i = 0; i < N; i++){
scanf("%lld/%lld", &a, &b);
maxFactor = gcd(a, b);
a /= maxFactor; b /= maxFactor; //将输入的分数化简
suma = a * sumb + b * suma; //将新接受的分数与以前累加的结果 求和
sumb = b * sumb;
maxFactor = gcd(suma, sumb);
suma /= maxFactor; sumb /= maxFactor; //将新的累加和化简
}
ll integer = suma / sumb;
suma %= sumb;
if(integer != 0 && suma != 0) printf("%lld %lld/%lld", integer, suma, sumb);
else if(integer != 0 && suma == 0) printf("%lld", integer);
else if(integer == 0 && suma != 0) printf("%lld/%lld", suma, sumb);
else printf("0");
return 0;
}

 

4,解题过程h

第一搏

这一题的关键是找最大公约数/最小公倍数,自然而然地想到了辗转相除法(但是又忘了怎么实现。。。)

参考​​@GardeniaMinwentel【c++中求两个数的最大公约数和最小公倍数(辗转相除法)】​

辗转相除法

  • 辗转相除法的核心就是用较大的数m去除较小的数n,如果刚好能整除,则m与n的最大公约数为n,如果不能整除,则将n的值赋给m,余数r赋给n,再进行下一次的相除,以此循环,直到整除为止;
  • 当r==0时,最后的n就是最大公约数
  • 原数x和y的最小公倍数:两者乘积除以最大公约数,x*y/n;

(看完大神们的代码之后,感觉自己low爆了。。。) 

#include<bits/stdc++.h>
using namespace std;

int findMaxFactor(int a, int b){//辗转相除法求最大公约数
if(a < b) swap(a, b);//a为较大值
int r = a % b;//余数
while(r){
a = b;
b = r;
r = a % b;
}
return b;
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
int nume[101], demo[101];
int N, pos;//N分数的个数 pos除号的位置
scanf("%d", &N);
bool sign;
string s, num, dem;
for(int i = 0; i < N; i++){
cin>>s;
if(s[0] == '-'){
s = s.substr(1);
sign = false;
}
else sign = true;
pos = s.find('/');
num = s.substr(0, pos);
dem = s.substr(pos + 1);
nume[i] = sign == true ? stoi(num) : -stoi(num);
demo[i] = stoi(dem);
//cout<<nume[i]<<' '<<demo[i]<<endl;
}
int maxFactor, leastCommon;//maxFactor最大公约数 leastCommon最小公倍数
if(N == 1) maxFactor = demo[0];//只有一个分数
else{
maxFactor = findMaxFactor(demo[0], demo[1]);
leastCommon = demo[0] * demo[1] / maxFactor;
}

for(int i = 2; i < N; i++){
maxFactor = findMaxFactor(leastCommon, demo[i]);
leastCommon = leastCommon * demo[i] / maxFactor;
}
int sum = 0;
for(int i = 0; i < N; i++){
sum += nume[i] * (leastCommon / demo[i]);
}
if(sum / leastCommon != 0) printf("%d", sum / leastCommon);
int x = sum % leastCommon;
if(x != 0){
maxFactor = findMaxFactor(x, leastCommon);
printf(" %d/%d", x / maxFactor, leastCommon / maxFactor);
}

return 0;
}

PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_1081_05

好烦这种题!!!

第二搏

后来注意到,题目中的数据类型是 long int,也就是int,但是我的算法是,所有分数同分母之后将分子乘以响应的倍数并相加,最后再将终极分子终极分母化简,可能是分子累加时超出了数据范围,于是我想把int换成long long,但还是不行。。。

PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_PAT_06

PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_C++_07

第三搏

参考了大神们的做法,,,每加一次,化简一次,才不会溢出!(而且学到了辗转相除法更简洁的表达方式)

注:long int其实指的是int类型,不过为了保险起见,仍使用long long。

采用柳神的方法,suma和sumb存放每次相加后的结果,若下一个分数为a/b,则将a/b化简后,suma*b+sumb*a作为新的分子,b*sumb作为新的分母;

由于可能是假分数,一开始,还担心会出现1234567899999111/13与1/123456相加,结果溢出的情况,不过从测试结果看,并不会出现这种情况。

PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】_1081_08