EOJ 1852 Solution Report --Ordered Fractions

代码看不清的请戳我的原文:http://linus-young.github.io/blog/2014/03/01/eoj-1852-solution-report-ordered-fractions/

csdn这排版没法用了,掀桌(╯‵□′)╯︵┻━┻


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;
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值