浅谈一类平面点对斜率最值问题

问题引入

给出平面个点 , 求出任意两点组成的直线中斜率最大的两个点.


分析

朴素的思路 : 枚举任意两个点 , 计算斜率最大的点对. 时间复杂度. 在比赛中往往不可行.


但是可以证明的是如果我们安装 x 坐标系轴排好序的话 斜率最大的点对一定在相邻二个点之间

假设排序得到了三点 A B C

1) A B C 三点共线   

2) A B C 三点不共线  


如图可以证明.


综上斜率最大两点一定在相邻两点之间.


例题

贝壳找房性价比

贝壳找房有一个性价比比较的系统,对于两个房源 aba 的价格为 pa 万元,面积 sa 平方米,b 的价格为 pb万元,面积为 sb 平方米。他们的绝对性价比差定义成为 sasb∣/papb

现在给出 n 个房源的价格和面积,请你求出一对房源使得它们的绝对性价比差最大。

输入格式

输入第一行一个整数 T 表示数据组数。

接下来输入 T 组数据,每一组数据按照下面格式输入。

第一行输入一个整数 n 表示房源的个数。

接下来 n 行,每行输入两个整数 si,pi,分别表示第 i 个房源的面积为 si 平方米,价格为 pi 万元。

数据保证 1T50,2n105,si,pi108,并且 没有任何两个房源的面积和价格都一样

输出格式

对于每组数据输出一行,如果该组数据的答案趋向于无穷大,输出 1,否则,输出最大的绝对性价比差。(所有输出误差在 106 以内都可以被接受)。

本题答案不唯一,符合要求的答案均正确

样例输入
2
4
1 3
4 5
7 8
3 6
2
4 10
4 11
样例输出
1.500000
-1
题目来源:

传送门

实际上就是在二维平面上找两个斜率绝对值最大的点。我们按照面积从小到达排序,通过各

种情况的分析,可以证明,斜率绝对值最大的两个点必然会相邻。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
struct Point {
    ll s,p;
    bool operator < (const Point &a) const{
        return s < a.s;
    }
};
Point points[maxn];
int main()
{
    int caset;scanf("%d",&caset);
    while(caset--) {
        int n;scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%lld %lld",&points[i].s,&points[i].p); 
        sort(points,points+n);
        double ans = 0;
        for(int i=1;i<n;i++) {
            if(points[i].s == points[i-1].s) { ans = -1;break;}
            ans = max(ans,fabs(1.0 * points[i].p - points[i-1].p)/fabs(1.0 * points[i].s - points[i-1].s));
        }
        if(ans == -1) printf("-1\n");
        else printf("%.6f\n",ans);
    }
    return 0;
}


坐标变换下的斜率最值

给出个点,求出任意两点的斜率最接近 的斜率值


这里我们引述上边讨论得到的最大斜率只需要按照 x 轴排序即可.斜率最大为   对应于与 y轴平行


现在要求斜率最接近 

那么我们构造新的坐标系 这里   


于是对于每个点(x,y) 我们得到其在坐标系下的坐标


因为这里是向下旋转了坐标系.如果向上需要重新计算坐标变化,即将

所以我们只需要对  进行排序即可

由归纳法,斜率最大(最接近 )的点对一定是在相邻的两个点中.

 


所以只需要按照 的大小进行排序即可


至此问题得到解决

例题

小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


根据以上讨论结果.

我们对进行排序 即对  进行排序

则保证最优斜率一定在相邻两点之间

代码

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值