北大POJ_1079_Ratio

来源:互联网 发布:电脑软件乱码 编辑:程序博客网 时间:2024/04/29 18:46

1. 1079——Ratio

1.1 题目

网址 http://poj.org/problem?id=1079

Ratio

Time Limit: 1000MS

Memory Limit: 10000K

Total Submissions: 3983

Accepted: 1483

Description

If you ever see a televised report on stock market activity,you'll hear the anchorperson say something like ``Gainers outnumbered losers 14to 9,'' which means that for every 14 stocks that increased in value that day,approximately 9 other stocks declined in value. Often, as you hear that, you'llsee on the screen something like this: 
Gainers 1498 
Losers 902 

As a person with a head for numbers, you'll notice that the anchorperson couldhave said ``Gainers outnumbered losers 5 to 3'', which is a more accurateapproximation to what really happened. After all, the exact ratio of winners tolosers is (to the nearest millionth) 1.660754, and he reported a ratio of 14 to9, which is 1.555555, for an error of 0.105199; he could have said ``5 to 3'',and introduced an error of only 1.666667-1.660754=0.005913. The estimate ``5 to3'' is not as accurate as ``1498 to 902'' of course; evidently, another goal isto use small integers to express the ratio. So, why did the anchorperson say``14 to 9?'' Because his algorithm is to lop off the last two digits of eachnumber and use those as the approximate ratio. 

What the anchorman needs is a list of rational approximations of increasingaccuracy, so that he can pick one to read on the air. Specifically, he needs asequence {a_1, a_2, ..., a_n} where a_1 is a rational number with denominator 1that most exactly matches the true ratio of winners to losers (rounding up incase of ties), a_{i+1} is the rational number with least denominator thatprovides a more accurate approximation than a_i, and a_n is the exact ratio,expressed with the least possible denominator. Given this sequence, theanchorperson can decide which ratio gives the best tradeoff between accuracyand simplicity. 

For example, if 5 stocks rose in price and 4 fell, the best approximation withdenominator 1 is 1/1; that is, for every stock that fell, about one rose. Thisanswer differs from the exact answer by 0.25 (1.0 vs 1.25). The bestapproximations with two in the denominator are 2/2 and 3/2, but neither is animprovement on the ratio 1/1, so neither would be considered. The bestapproximation with three in the denominator 4/3, is more accurate than any seenso far, so it is one that should be reported. Finally, of course, 5/4 isexactly the ratio, and so it is the last number reported in the sequence. 

Can you automate this process and help the anchorpeople? 

Input

input contains several pairs of positive integers. Each pair is ona line by itself, beginning in the first column and with a space between thetwo numbers. The first number of a pair is the number of gaining stocks for theday, and the second number is the number of losing stocks for the day. Thetotal number of stocks never exceeds 5000.

Output

Foreach input pair, the standard output should contain a series of approximationsto the ratio of gainers to losers. The first approximation has '1' asdenominator, and the last is exactly the ratio of gainers to losers, expressedas a fraction with least possible denominator. The approximations in betweenare increasingly accurate and have increasing denominators, as described above. 
The approximations for a pair are printed one to a line, beginning in columnone, with the numerator and denominator of an approximation separated by aslash (``/''). A blank line separates one sequence of approximations fromanother. 

Sample Input

5 4 
1498 902 

Sample Output

1/1 
4/3 
5/4 
 
2/1 
3/2 
5/3 
48/29 
53/32 
58/35 
63/38 
68/41 
73/44 
78/47 
83/50 
88/53 
93/56 
377/227 
470/283 
563/339 
656/395 
749/451

Source

South Central USA 1998

 1.2 题目分析

(1)本题为分母逼近问题,直接模拟分数计算和分数差值比较即可,不必采用高精度计算。可以让分母从1递增到分母最大值(den_init) ,逐次逼近原始精确比例。分别求出当分母是1,2,3,4……den_init时,最接近原始精确比例(ratio=num_init/den_init)的分数值。

1.3 题目解答

1.3.1 C++方法1——分母从1加到max,逐次逼近

#include <iostream>using namespace std;/**********************************//***北大POJ 1079 Ratio*************//***modified by lbs  2015.8.5******//**********************************//**********************************//*****denominator   分母***********//*****numerator     分子***********//**********************************/int main(void){int den_init,num_init;  //分母和分子的输入数值  初始数值int den, num;  //分母和分子int num_temp;   //计算过程中分子的临时变量int i = 0;while (cin >> num_init >> den_init)  //先输入分子,后输入分母{num = 1;   //init  分子初始化为1den = 0;   //init  分母初始化为0for (i = 1; i <= den_init; i++)   //分母从1到max递增,逐次逼近{num_temp = (int)((double)(i*num_init) / (double)den_init + 0.5);  //分子临时变量if (den*abs(num_init*i - den_init*num_temp) < i*abs(num_init*den - den_init*num)){num = num_temp;den = i;cout << num << "/" << den << endl;}if (den_init*num == num_init*den) break;   //相等情况下 停止循环}cout << endl;}//system("pause");return 0;}

备注: 

(1)     分数比较大小    

  a. 判断两个分数是否相等,用B*C==A*D更好,因为这样可以很方便的排除分母为0的特殊情况。并且,对于double类型的无限小数,其相等的精度建立在指定的有效位数的基础上。不用除法,而用相乘判断是否相等,可以避免有效位数带来的问题。

 b. 同理,判断两个分数是否相等,可以通过判断来判断。(分数通分,作差,取分子)

(2)     初始化时,分母初始化为0,分子初始化为1

 a.  if(den*abs(num_init*i - den_init*num_temp) < i*abs(num_init*den -den_init*num))

此语句中不能取”<=”,只能取”<”。因为题目中规定,若对精度没有提高,则不计入输出中。即:The best approximations with two in the denominator are 2/2 and 3/2,but neither is an improvement on the ratio 1/1, so neither would be considered.

 b. 若输入1/3,则第一个输出分数为0/1。由上分析知,”if(den*abs(num_init*i - den_init*num_temp) < i*abs(num_init*den -den_init*num))”中只能取”<”,故初始化时,不能初始化为0/1,否则二者相等,不会输出0/1。因此,应该初始化为den=0,num=1或den=0,num=0

(3)     num_temp =(int)((double)(i*num_init) / (double)den_init + 0.5);

  C++中,(int)强制转换,并不是四舍五入,而是取不大于参数的且最接近参数的int整数。为了达到四舍五入的效果,可以在参数末尾加上0.5,再进行强制转换。

1.3.2 JAVA

package TEST;import java.io.*;public class TEST{//辗转相除法求最大公约数public static int gcd(int a,int b){int change;int temp;//a=max b=minif(a<b){change=a;a=b;b=change;}while(a%b!=0){temp=a;a=b;b=temp%b;}return b;}public static void main(String[] args) throws IOException{StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));while(in.nextToken()!=StreamTokenizer.TT_EOF){int a,b;double last=100000;a=(int)in.nval;in.nextToken();b=(int)in.nval;int devide=gcd(a,b);if(devide!=1) {a/=devide;b/=devide;}for(int i=1;i< b;i++){double up=(double)a/((double)b/(double)i);int tup1=(int)up;    int tup2=tup1+1;    int upf;    if(Math.abs((double)tup1/i-(double)a/b)< Math.abs((double)tup2/i-(double)a/b))    {    upf=tup1;    }    else     {    upf=tup2;    }    if(Math.abs((double)upf/i-(double)a/b)< last)    {    last=Math.abs((double)upf/i-(double)a/b);    System.out.println(upf+"/"+i);    }}System.out.println(a+"/"+b);System.out.println();}}}

备注:

(1)辗转相除法求最大公约数

http://baike.baidu.com/link?url=2tgxo5BqB8rpWvsamSq0MYIX3uO7LRXPMlMY4_iqdT8Er7UPtaZNNY8F3pX8cEHICaNM8XIgX8SjtsUsEbx7Sq

辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数。

设两数为a、b(a>b),求a和b最大公约数(a,b)的步骤如下:用a除以b,得a÷b=q......r1(0≤r1)。若r1=0,则(a,b)=b;若r1≠0,则gcd(a,b)=gcd(b,r1),再用b除以r1,得b÷r1=q......r2(0≤r2).若r2=0,则(a,b)=r1,若r2≠0,则gcd(b,r1)=gcd(r1,r2),继续用r1除r2,……如此下去,直到能整除为止(余数为0)。其最后除数即为gcd(a, b)。

 a. 算法实现

     i.  C++

 int gcd(int a,int b){int change;int temp;//a=max b=minif(a<b){change=a;a=b;b=change;}while(a%b!=0){temp=a;a=b;b=temp%b;}return b;}

      ii. JAVA

public static int gcd(int a,int b){int change;int temp;//a=max b=minif(a<b){change=a;a=b;b=change;}while(a%b!=0){temp=a;a=b;b=temp%b;}return b;}

1.4 参考资料

[1] http://blog.163.com/happyliyifan@126/blog/static/3746277220131120243584/

[2] http://www.cppblog.com/varg-vikernes/archive/2010/02/13/107775.html

[3] http://www.java3z.com/cwbwebhome/article/article17/acm126.html?id=3133

0 0
原创粉丝点击