题目描述
小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 斜率,即答案必存在于相邻的两点中。
枚举相邻两点的斜率,比较得到答案。
代码:
#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 (Q * y - P * x) < Q * a.y - P * a.x; } }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; }