写在前面:
关于分数型的题目求解,我觉得晴神在《算法笔记》上写得很好 真的是简单易懂(吹爆晴神 因为我是晴神的小迷弟?)。《算法笔记》里分数是用结构体存储的,然后有一系列的自定义函数:分数的加减乘除以及化简和输出。我觉得只需要在理解的基础上对晴神的这套模板加以记忆,对以后求解有关分数的题目是很有帮助的。
晴神的《算法笔记》电子版链接:https://pan.baidu.com/s/1NV5g1fb1iwM2aglFyO7N-Q 提取码:ycat。最好还是买纸质版的哦,京东和淘宝均有卖的,我是直接在机械工业出版社买的:https://item.jd.com/11973614.html。
分数模板:
struct Fraction //分数用一个结构体来存储 { int up; //分子 int down; //分母 }; int gcd(int a, int b) //用辗转相除法来递归求最大公约数 { return !b ? a : gcd(b, a%b); //上面这一行等价于下面这段代码 // if(b == 0) // { // return a; // } // else // { // return gcd(b, a%b); // } } Fraction reduction(Fraction result) //分数的化简 { if(result.down < 0) //若分母为负数,则令分子和分母都变为相反数 { result.up = -result.up; result.down = -result.down; } if(result.up == 0) //若分子为0,则令分母为1 { result.down = 1; } else //若分子不为0,则进行约分 { int d = gcd(abs(result.up), abs(result.down)); //分子、分母的最大公约数为d //约去最大公约数 result.up /= d; result.down /= d; } return result; } Fraction add(Fraction f1, Fraction f2) //分数的加法 { Fraction result; result.up = f1.up*f2.down + f1.down*f2.up; //分数和的分子 result.down = f1.down*f2.down; //分数和的分母 return reduction(result); //将分数和化简后,返回结果分数 } Fraction minus(Fraction f1, Fraction f2) //分数的减法 { Fraction result; result.up = f1.up*f2.down - f1.down*f2.up; //分数差的分子 result.down = f1.down*f2.down; //分数差的分母 return reduction(result); //将分数差化简后,返回结果分数 } Fraction multiply(Fraction f1, Fraction f2) //分数的乘法 { Fraction result; result.up = f1.up*f2.up; //分数积的分子 result.down = f1.down*f2.down; //分数积的分母 return reduction(result); //将分数积化简后,返回结果分数 } Fraction divide(Fraction f1, Fraction f2) //分数的除法 { Fraction result; result.up = f1.up*f2.down; //分数商的分子 result.down = f1.down*f2.up; //分数商的分母 return reduction(result); //将分数商化简后,返回结果分数 } void showResult(Fraction result) //输出分数 { result = reduction(result); //化简分数 if(result.down == 1) //整数 { printf("%d\n", result.up); } else if(abs(result.up) > abs(result.down)) //假分数 { printf("%d %d/%d\n", result.up/result.down, abs(result.up)%result.down, result.down); } else //真分数 { printf("%d/%d\n", result.up, result.down); } }
就拿之前HBU训练营中出现的那道“有理数的均值”为例吧。
题目描述:
本题要求编写程序,计算N个有理数的平均值。
输入格式:
输入第一行给出正整数N(≤100);第二行中按照a1/b1 a2/b2 …的格式给出N个分数形式的有理数,其中分子和分母全是整形范围内的整数;如果是负数,则负号一定出现在最前面。
输出格式:
在一行中按照a/b的格式输出N个有理数的平均值。注意必须是该有理数的最简分数形式,若分母为1,则只输出分子。
输入样例1:
4
1/2 1/6 3/6 -5/10
输出样例1:
1/6
输入样例2:
2
4/3 2/3
输出样例2:
1
解题思路:
先判断是不是第一次输入,如果是第一次输入就把本次输入的分数赋值给sum,否则调用自定义函数add来对输入的分数进行累加。然后用分数总和sum除以分数个数N来求平均值,这里可以直接把N写成一个分母为1、分子为N的分数。最后化简输出结果即可。晴神??!
AC代码:
#include <bits/stdc++.h> using namespace std; struct Fraction //分数用一个结构体来存储 { int up; //分子 int down; //分母 }; int gcd(int a, int b) //用辗转相除法来递归求最大公约数 { return !b ? a : gcd(b, a%b); } Fraction reduction(Fraction result) //分数的化简 { if(result.down < 0) //若分母为负数,则令分子和分母都变为相反数 { result.up = -result.up; result.down = -result.down; } if(result.up == 0) //若分子为0,则令分母为1 { result.down = 1; } else //若分子不为0,则进行约分 { int d = gcd(abs(result.up), abs(result.down)); //分子、分母的最大公约数为d //约去最大公约数 result.up /= d; result.down /= d; } return result; } Fraction add(Fraction f1, Fraction f2) //分数的加法 { Fraction result; result.up = f1.up*f2.down + f1.down*f2.up; //分数和的分子 result.down = f1.down*f2.down; //分数和的分母 return reduction(result); //将分数和化简后,返回结果分数 } Fraction divide(Fraction f1, Fraction f2) //分数的除法 { Fraction result; result.up = f1.up*f2.down; //分数商的分子 result.down = f1.down*f2.up; //分数商的分母 return reduction(result); //将分数商化简后,返回结果分数 } void showResult(Fraction result) //输出分数 { result = reduction(result); //化简分数 if(result.down == 1) //整数 { printf("%d\n", result.up); } else if(abs(result.up) > abs(result.down)) //假分数 { printf("%d %d/%d\n", result.up/result.down, abs(result.up)%result.down, result.down); } else //真分数 { printf("%d/%d\n", result.up, result.down); } } int main() { int N; cin >> N; Fraction sum; //分数总和 bool isVirgin = true; //判断是不是第一次 for(int i = 0; i < N; i++) { Fraction temp; scanf("%d/%d",&temp.up,&temp.down); if(isVirgin) //若是第一次 { sum.up = temp.up; sum.down = temp.down; isVirgin = false; } else { sum = add(sum,temp); } } Fraction f; f.up = N; f.down = 1; showResult(reduction(divide(sum,f))); return 0; }