Wannafly挑战赛15 E 小W的斜率

题目描述 

小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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值