PAT_甲级_1081 Rational Sum (20point(s)) (C++)【字符串处理/辗转相除法/分数累加和】
原创
©著作权归作者所有:来自51CTO博客作者再见萤火虫IT的原创作品,请联系作者获取转载授权,否则将追究法律责任
目录
1,题目描述
题目描述
2,思路
注意
算法
3,AC代码
4,解题过程h
第一搏
辗转相除法
第二搏
第三搏
1,题目描述
- rational:合理的; 理性的; 明智的; 理智的; 清醒的;
- numerator:(分数中的) 分子;
- denominator:分母;
Sample Input 1:
Sample Output 1:
Sample Input 2:
Sample Output 2:
Sample Input 3:
Sample Output 3:
题目描述
将给出的所有分数求和,并化简为最简形式。
2,思路
注意
- 由于数据比较大,统一通分之后再化简可能会溢出,需要加一次化简一次;
- 每次读入的分数可能不是最简形式的,需要先化简,尽可能地避免溢出;
- 最小公倍数 =x * y / 最大公因数;
- 递归求最大公因数时,要输出绝对值;
算法
- 设计函数gcd,求任意两数的最大公因数:
- 每接收一个分数,将其化简为最简形式,并与以前的累加和相加,在对累加和进行化简:
- 分情况对最终结果进行输出:
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;
}
好烦这种题!!!
第二搏
后来注意到,题目中的数据类型是 long int,也就是int,但是我的算法是,所有分数同分母之后将分子乘以响应的倍数并相加,最后再将终极分子和终极分母化简,可能是分子累加时超出了数据范围,于是我想把int换成long long,但还是不行。。。
第三搏
参考了大神们的做法,,,每加一次,化简一次,才不会溢出!(而且学到了辗转相除法更简洁的表达方式)
注:long int其实指的是int类型,不过为了保险起见,仍使用long long。
采用柳神的方法,suma和sumb存放每次相加后的结果,若下一个分数为a/b,则将a/b化简后,suma*b+sumb*a作为新的分子,b*sumb作为新的分母;
由于可能是假分数,一开始,还担心会出现1234567899999111/13与1/123456相加,结果溢出的情况,不过从测试结果看,并不会出现这种情况。