题目描述:

http://ace.delos.com/usacoprob2?a=natOyh2BvAP&S=frac1

输入一个自然数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的两个自然数。

 

水题一道,本来不想多说,只是直接AC掉之后,看了一些别人的思路,感觉有一些优化的地方值得注意。这个题目第一眼就能猜到肯定有神马数学方法能找到直接构造的思路,不过由于本人数学很挫,看了半天没找到神马规律。最后无奈,就直接脑残的枚举每一个分数,先自动约分成最简分数,然后再输入到vector数组中,然后再排序,输出时过滤重复的。

其实我刚才的那个暴力脑残方法就有很多优化的地方,比如,在枚举的过程中,如果分子分母有大于1的最大公约数,那么这个分数组合就不用插进vector数组中,因为肯定会有其值相同但是分子分母互质的分数组合插入到vector中,比如,当我插入6/8的时候,是先降起化简为3/4,然后将其插入vector,实际上根本就不用插入,因为之前3/4已经被插入了。所以,在枚举的过程中,只要是能约分的,就不插入,直接掠过。这样的话,一来缩小了排序规模,二来数组里面就没有重复的分数了,输出的时候就不用过滤已经存在的值了。

在测试约分的时候用的是辗转相除算出最大公约数,如果大于1则说明分子分母不互质,但是对于两个数都是偶数的话,就可以省略此辗转过程,这点优化,可以缩小1/4的辗转相除此数,当枚举量很大的时候还蛮有效的。

标准答案的第二种方法是我最初试图寻找的数学方法,看完之后才发现自己弱暴了,其中的道理现在还不会证明。。。以后有空琢磨琢磨。。。

Here's a super fast solution from Russ:

We notice that we can start with 0/1 and 1/1 as our ``endpoints'' and recursively generate the middle points by adding numerators and denominators.

0/1                                                              1/1
                               1/2
                  1/3                      2/3
        1/4              2/5         3/5                 3/4
    1/5      2/7     3/8    3/7   4/7   5/8       5/7         4/5

Each fraction is created from the one up to its right and the one up to its left. This idea lends itself easily to a recursion that we cut off when we go too deep.

这样的话就可以递归实现了,先不说人家算法复杂度上能从nlogn缩小到n,在代码的实现上,也会变的短小精悍。。。直接一个就搞定了。。。 

其实还有一种比较不错的方法,就是离散化之后进行“桶排序”,桶排序是复杂度为n的排序方法,不过其使用很受限制,而本题目如果通过一个巧妙的转化就可以使用桶排序拉 !具体算法直接摘自USACO training(很不错的ACM平台 !)

由于任意两个分数的差一定>=1/(160*159),所以直接把所有分数乘上50880,四舍五入后直接桶排序,而且一边输入一边排,只需要同时记录分子分母就行了,O(n)!!。连化简都省了,遇到相同值就跳过(因为之前必定有一个数值更小的分数等于这个值)

View Code
/*
 ID:liuweiv2
 PROG:frac1
 LANG:C++
 */



#include <iostream>
#include<algorithm>
#include<vector>
#include<fstream>

using namespace std;

ifstream infile;
ofstream outfile;

int n;

struct Num {
    
    int zi;
    int mu;
    
    bool operator < (const Num &a) const{
        return zi*a.mu < mu * a.zi;
    }
    

};
void yueFun(Num & num){
    int tempZi = num.zi;
    int tempMu = num.mu;
    int last = tempMu % tempZi;
    while(last){
        tempMu = tempZi;
        tempZi = last;
        last = tempMu % tempZi;
    }
    num.zi/=tempZi;
    num.mu/=tempZi;
    
};

void print(Num num){
    outfile<<num.zi<<"/"<<num.mu<<endl;
}

vector<Num>nums;

int main(int argc, const char * argv[])
{
    
    infile.open("frac1.in");
    outfile.open("frac1.out");
    infile>>n;
    Num num;
    num.zi = 0;num.mu = 1;nums.push_back(num);
    num.zi = 1;num.mu = 1;nums.push_back(num);
    
    
    for(int i=2;i<=n;i++)
        for(int j=1;j<i;j++){
            num.zi = j;
            num.mu = i;
            yueFun(num);
            nums.push_back(num);
        }
    sort(nums.begin(),nums.end());
    
    int a = -1,b = -1;
    for(int i=0;i<nums.size();i++){
        if(a == nums[i].zi && b == nums[i].mu)
            continue;
        else{
            a = nums[i].zi;
            b = nums[i].mu;
            print(nums[i]);
        }
    }

     

    return 0;

    
}

 

posted on 2012-06-07 12:02  geeker  阅读(652)  评论(0编辑  收藏  举报