whyisyoung's Blog

If you can not persist, you will never succeed :)

EOJ 1852 Solution Report --Ordered Fractions

| Comments

1. 问题描述

给定一个数 N , 从小到大列出 0 到 1 之间 分母不超过 N 的最简真分数,包括 0/1, 1/1

2. 解题思路

### 解法1 最简单的想法,生成所有有效分数,然后对其排序,输出。(0/1 和 1/1 可单独输出) 那么什么是有效分数呢,需要满足以下条件:

  • 分母不超过 N
  • 分子与分母互质
  • 分子小于分母

所以我们用如下循环即可得到有效分数:

1
2
3
4
for(int i = 1; i <= n-1; i++)
	for(int j = i+1; j <= n; j++)
		if(gcd(i, j) == 1) // gcd denotes 最大公约数
            /* store valid fractions here*/

那么问题是怎么存取分数呢?而且输出格式为: i/j 很容易想到 struct 结构体。 因此我们定义如下结构体来存取分数:

1
2
3
4
struct fraction {
	int numerator;
	int denominator;
};

全部有效分数获得后,排序也很简单了,用个 qsort 或者 sort 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//
//  main.cpp
//  eoj1852_struct
//
//  Created by whyisyoung on 2/27/14.
//  Copyright (c) 2014 whyisyoung. All rights reserved.
//

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
using namespace std;

struct fraction {
	int numerator;
	int denominator;
}frac[7820];

int gcd(int a, int b)
{
	return (b == 0) ? a : (gcd(b, a % b));
}

int generate_fractions(int n)
{
	int count = 0;

	for(int i = 1; i <= n-1; i++) {
		for(int j = i+1; j <= n; j++) {
			if(gcd(i, j) == 1) {
				frac[count].numerator 	  = i;
				frac[count++].denominator = j;
			}
		}
	}
	return count;
}

int compare(const void* a, const void* b)
{
	fraction *x = (fraction *)a;
	fraction *y = (fraction *)b;
	double frac_a = x->numerator * 1.0 / x->denominator;
	double frac_b = y->numerator * 1.0 / y->denominator;
	if(frac_a < frac_b)
		return -1;
	if(frac_a > frac_b)
		return 1;
	return 0;
}

int main(int argc, char const *argv[])
{
	int n, count;

	while(scanf("%d", &n) != EOF) {
		count = generate_fractions(n);

		cout << "0/1\n";

		qsort(frac, count, sizeof(frac[0]), compare);

		for(int i = 0; i < count; i++)
			cout << frac[i].numerator << '/' << frac[i].denominator << endl;

		cout << "1/1\n";
	}
	return 0;
}

解法2

其实一开始想到的是把 “i/j” 当作字符串处理的,于是问题就是找小数和字符串的对应关系,这好办,一个 map<double, string>就搞定了,而且由于 map 默认对 key 按升序排序,所以只要把这些有效分数加入到 map 即可,连排序的工作都省了,当然为此付出的代价就是多消耗了一些 memory。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//
//  main.cpp
//  eoj1852
//
//  Created by whyisyoung on 2/23/14.
//  Copyright (c) 2014 whyisyoung. All rights reserved.
//

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cstdlib>
#include <string>
#include <cstring>
using namespace std;

map<double, string> frac;

int gcd(int a, int b)
{
	return (b == 0)?a:(gcd(b, a % b));
}

int generate_fractions(int n)
{
    int size = 0;

    for (int i = 1; i <= n-1; ++i) {
		for(int j = i+1; j <= n; j++) {
			if(gcd(i, j) == 1) {
				char cstr[10];
				sprintf(cstr, "%d/%d", i, j); // itoa(i, cstr, 10) is deprecated
				string str(cstr);
				frac[i*1.0 / j] = str;
				size++;
            }
		}
	}
	return size;
}

int main(int argc, char const *argv[])
{
	int n, size;
	while(cin >> n) {
		size = generate_fractions(n);

		cout << "0/1\n";

		map<double, string>::iterator it = frac.begin();

		for( ; it != frac.end(); it++) {
			cout << it->second << endl;
		}

		cout << "1/1\n";
	}
	return 0;
}

解法3

以前肖老师讲的,这题实际就是要输出 farey 数列 有两种生成办法:

递归生成:

1
2
3
4
5
6
7
8
9
int N;
void Feray(int a, int b, int c, int d)
{
	if(b + d > N)
		return ;
	Feray(a, b, a+c, b+d);
	cout << a+c << '/' << b+d << endl;
	Feray(a+c, b+d, c, d);
}

主函数中只需调用这句即可 Feray(0, 1, 1, 1);

迭代生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void farey(int n)
{
	int pa=0, pb=1, ca=1, cb=n;
	int na, nb;

	printf("%d/%d\n", pa, pb);
	printf("%d/%d\n", ca, cb);
	while(ca!=cb){
		na = (pb+n) / cb * ca - pa;
		nb = (pb+n) / cb * cb - pb;
		printf("%d/%d\n", na, nb);
		pa = ca;
		pb = cb;
		ca = na;
		cb = nb;
	}
}

Comments