北大POJ_1079_Ratio

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  分子初始化为1
		den = 0;   //init  分母初始化为0

		for (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=min
		if(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=min
		if(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=min
		if(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

西北工业大学NOJC程序设计习题答案(非本人制作,侵删) 1.“1“的传奇 2.A+B 3.A+BⅡ 4.AB 5.ACKERMAN 6.Arithmetic Progressions 7.Bee 8.Checksum algorithm 9.Coin Test 10.Dexter need help 11.Double 12.Easy problem 13.Favorite number 14.Graveyard 15.Hailstone 16.Hanoi Ⅱ 17.Houseboat 18.Music Composer 19.Redistribute wealth 20.Road trip 21.Scoring 22.Specialized Numbers 23.Sticks 24.Sum of Consecutive 25.Symmetric Sort 26.The Clock 27.The Ratio of gainers to losers 28.VOL大学乒乓球比赛 29.毕业设计论文打印 30.边沿与内芯的差 31.不会吧,又是A+B 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.计算A+B 68.计算PI 69.计算π 70.计算成绩 71.计算完全数 72.检测位图长宽 73.检查图像文件格式 74.奖金发放 75.阶乘合计 76.解不等式 77.精确幂乘 78.恐怖水母 79.快速排序 80.粒子裂变 81.链表动态增长或缩短 82.链表节点删除 83.两个整数之间所有的素数 84.路痴 85.冒泡排序 86.你会存钱吗 87.逆序整数 88.排列 89.排列分析 90.平均值函数 91.奇特的分数数列 92.求建筑高度 93.区间内素数 94.三点顺序 95.山迪的麻烦 96.删除字符 97.是该年的第几天 98.是该年的第几天? 99.数据加密 100.搜索字符 101.所有素数 102.探索合数世纪 103.特殊要求的字符串 104.特殊整数 105.完全数 106.王的对抗 107.危险的组合 108.文件比较 109.文章统计 110.五猴分桃 111.小型数据库 112.幸运儿 113.幸运数字”7“ 114.选择排序 115.寻找规律 116.循环移位 117.延伸的卡片 118.羊羊聚会 119.一维数组”赋值“ 120.一维数组”加法“ 121.勇闯天涯 122.右上角 123.右下角 124.圆及圆球等的相关计算 125.圆及圆球等相关计算 126.程序员添加行号 127.找出数字 128.找幸运数 129.找最大数 130.整数位数 131.重组字符串 132.子序列的和 133.子字符串替换 134.自然数立方的乐趣 135.字符串比较 136.字符串复制 137.字符串加密编码 138.字符串逆序 139.字符串排序 140.字符串替换 141.字符串左中右 142.组合数 143.最次方数 144.最大乘积 145.最大整数 146.最小整数 147.最长回文子串 148.左上角 149.左下角
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值