【问题描述】
输入自然数N,对于一个最简分数 a/b(分子和分母互质的分数),满足1 <= b <= N,0 <= a/b <= 1,请找出所有满足条件的分数。
这有一个例子,当N=5时,所有解为:
0/1 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1/1
给定一个自然数N,1<=n<=160,请编程按分数值递增的顺序输出所有解。
提示:
①、0 和任意自然数的最大公约数就是那个自然数。
②、互质指最大公约数等于1的两个自然数。
【输入格式】
单独的一行 一个自然数N(1..160)
【输出格式】
每个分数单独占一行,按照大小次序排列 。
【输入样例】
5
【输出样例】
0/1
1/5
1/4
1/3
2/5
1/2
3/5
2/3
3/4
4/5
1/1
【数据范围】
1 <= N <= 160
【思路梳理】
一个比较简单的排序问题,只需要用一个结构体存储分子和分母就可以,然后用sort排序即可。实际上我们连分数值都不需要存储。判断互质直接使用gcd(共产党)辗转相除算法即可。
一个隐含的陷阱就是:当我们在用sort排序的时候,如果自定义的比较函数cmp直接比较的是分数值而不是两个分数交叉相乘的结果时,一旦n较大,会容易出现系统默认的精确度不够的情况(例如把1/159和1/160的计算结果认为是一样的)。
【Cpp代码】
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct data
{
int a,b;
};
vector<data>a;
int n;
int gcd(int x,int y)//辗转相除法,也就是著名的欧几里得算法
{
if(y==0) return x;
else return gcd(y,x%y);
}
bool cmp(data x,data y){ return x.a*y.b<x.b*y.a; }
int main()
{
//freopen("in.txt","r",stdin);
cin>>n;
a.push_back((data){0,1,0});//循环从i=1,j=1开始,所以这一个要手动加入
for(int j=1;j<=n;j++)
for(int i=1;i<=j;i++)
{
int g=gcd(i,j);
if(g==1) a.push_back((data){i,j,i*1.0/j});
//最大公约数为1就加入
}
sort(a.begin(),a.end(),cmp);
for(int i=0;i<a.size();i++)
printf("%d/%d\n",a[i].a,a[i].b);
return 0;
}