题目描述
小W非常喜欢某个有理数P/Q,而且他非常喜欢用它来进行一些玄学操作。他在二维平面上撒下了n个点,这些点相互不同,但一番观察之后,他失望的发现并没有任何两个点连成的直线的斜率是P/Q。你能不能告诉他在这些斜率中最接近P/Q的是多少。
输入描述:
第一行包括三个正整数n,P,Q(5<=n<=106,1<=P,Q<=105)
接下来n行,每行两个正整数x,y表示点的坐标(0<=x,y<=109)
输出描述:
一个有理数P’/Q’表示最接近P/Q的斜率
示例1
输入
6 15698 17433 112412868 636515040 122123982 526131695 58758943 343718480 447544052 640491230 162809501 315494932 870543506 895723090
输出
193409386/235911335
备注:
保证答案唯一,且P’/Q’>0 斜率为无穷时可表示为1/0
官方题解:
虽然题目描述的是斜率最接近,但实际上和求角度最接近没有太大的差别。
只不过两边最接近比较不太一样。所以可以不妨先旋转坐标系或者说把点按照 y-kx 排序,把问题转化为斜率绝对值最大的两个点,因为保证了不存在斜率等于所求值,所以不存在斜率是无穷
的情况。现在证明 X 相邻的两个点一定存在斜率最大。设点 A,B 是所求最接近斜
率,且 A,B 不相邻,其中间存在 C,AB 斜率可表示为(Ay-By)/(Ax-Bx)=((Ay-Cy)+(Cy-
By))/((Ax-Cx)+(Cx-Bx))所以在(Ay-Cy)/(Ax-Cx)和(Cy-By)/(Cx-Bx)两项中必然存在
一项大于等于 AB 斜率,即答案必存在于相邻的两点中。
枚举相邻两点的斜率,比较得到答案。
题解:
给出个点,求出任意两点的斜率最接近 的斜率值
这里我们引述上边讨论得到的最大斜率只需要按照 x 轴排序即可.斜率最大为 对应于与 y轴平行
现在要求斜率最接近
那么我们构造新的坐标系 这里
于是对于每个点(x,y) 我们得到其在坐标系下的坐标
而
因为这里是向下旋转了坐标系.如果向上需要重新计算坐标变化,即将
所以我们只需要对 进行排序即可
由归纳法,斜率最大(最接近 )的点对一定是在相邻的两个点中.
所以只需要按照 的大小进行排序即可
至此问题得到解决
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
int n;
ll P,Q;
struct Point
{
ll x,y;
bool operator < (const struct Point &a) {
return (x * P - y * Q) < (a.x * P - a.y * Q);
}
}points[maxn];
int main()
{
while(~scanf("%d%lld%lld",&n,&P,&Q))
{
for(int i=0;i<n;i++) scanf("%lld%lld",&points[i].x,&points[i].y);
sort(points,points+n);
ll up = 0,down = 1;
for(int i=1;i<n;i++)
{
if(points[i].x == points[i-1].x) continue;
double temp = double(points[i].y - points[i-1].y) / (points[i].x - points[i-1].x);
double rate = double(up) / down;
double ok = double(P) / Q;
if(fabs(rate - ok) > fabs(temp - ok))
{
up = points[i].y - points[i-1].y;
down = points[i].x - points[i-1].x;
}
}
ll gcd = __gcd(up,down);
printf("%lld/%lld\n",up/gcd,down/gcd);
}
return 0;
}