TYVJ 1077 有理逼近 解题报告

  看了网上的一个人的代码,那个人是对分母循环,然后确定分子,然后搜最优解。但是他找分子的时候是考虑了一些范围的,想一下可以直接去掉这层考虑,等下说明,然后它用了gcd(最大公约数),我这里也把它去掉了,不过其实都不保险,因为吧,等下分析下:

  利用的是j/i≈sqrt(p),可以写成j≈i*sqrt(p),然后i从1到n枚举,求j,然后判断j/i,这就是思路。
  首先是要比sqrt(p)小的啊,i*sqrt(p)是分子,但是i*sqrt(p)是分数,而我们需要的是整数,那该怎么办,搜两个,一个是(int)(i*sqrt(p)),另一个是(int)(i*sqrt(p)) - 1,证明如下:
  i*sqrt(p)是xxxx.xxxx,如果小数点后面都是零,那么i*sqrt(p)就是xxxx,那要取得数就是(int)(i*sqrt(p)) - 1,不然的话直接把小数点去掉就是最接近(int)(i*sqrt(p))的数了。
  可以类似的证明,取大于sqrt(p)的要取(int)(i*sqrt(p)), (int)(i*sqrt(p)) + 1。
  然后我为什么不约分呢,因为我是从1到n开始循环的,如果j/i要约分的话,设约分后为a/b,那么明显b<i,那么在循环到i之前,b必定已经被枚举了,可以知道j/i=a/b,那j/i就不会更新答案了,所以不需要约分。

  代码如下:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	int i, j;
	int p, n;
	int a, b, c, d;
	int beg, end;
	double s, max = 0, min = 300000, t;
	scanf("%d%d", &p, &n);
	s = sqrt(p);
	for(i = 1; i <= n; i++){
		beg = i * s - 1;
		end = beg + 1;
		if(beg <= 0){
			beg = 1;
		}
		if(end > n){
			end = n;
		}
		for(j = beg; j <= end; j++){
			t = (double)(j) / i;
			if(t < s && t > max){
				a = j, b = i;
				max = t;
			}
		}
	}
	for(i = 1; i <= n; i++){
		beg = i * s;
		end = beg + 1;
		if(beg <= 0){
			beg = 0;
		}
		if(end > n){
			end = n;
		}
		for(j = beg; j <= end; j++){
			t = (double)(j) / i;
			if(t > s && min > t){
				c = j, d = i;
				min = t;
			}
		}
	}
	printf("%d/%d %d/%d\n", a, b, c, d);
	return 0;
}
posted @ 2011-07-16 15:32  zqynux  阅读(282)  评论(0编辑  收藏  举报