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

目录

1,题目描述

 题目描述

2,思路

注意

算法

3,AC代码

4,解题过程h

第一搏

辗转相除法

第二搏

第三搏


1,题目描述

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

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. 每接收一个分数,将其化简为最简形式,并与以前的累加和相加,在对累加和进行化简:
  3. 分情况对最终结果进行输出:

 

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相加,结果溢出的情况,不过从测试结果看,并不会出现这种情况。

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值