问题引入
给出平面个点 , 求出任意两点组成的直线中斜率最大的两个点.
分析
朴素的思路 : 枚举任意两个点 , 计算斜率最大的点对. 时间复杂度. 在比赛中往往不可行.
但是可以证明的是如果我们安装 x 坐标系轴排好序的话 斜率最大的点对一定在相邻二个点之间
假设排序得到了三点 A B C
2) A B C 三点不共线
如图可以证明.
综上斜率最大两点一定在相邻两点之间.
例题
贝壳找房性价比
贝壳找房有一个性价比比较的系统,对于两个房源 a,b,a 的价格为 pa 万元,面积 sa 平方米,b 的价格为 pb万元,面积为 sb 平方米。他们的绝对性价比差定义成为 ∣sa−sb∣/∣pa−pb∣。
现在给出 n 个房源的价格和面积,请你求出一对房源使得它们的绝对性价比差最大。
输入格式
输入第一行一个整数 T 表示数据组数。
接下来输入 T 组数据,每一组数据按照下面格式输入。
第一行输入一个整数 n 表示房源的个数。
接下来 n 行,每行输入两个整数 si,pi,分别表示第 i 个房源的面积为 si 平方米,价格为 pi 万元。
数据保证 1≤T≤50,2≤n≤105,∣si∣,∣pi∣≤108,并且 没有任何两个房源的面积和价格都一样。
输出格式
对于每组数据输出一行,如果该组数据的答案趋向于无穷大,输出 −1,否则,输出最大的绝对性价比差。(所有输出误差在 10−6 以内都可以被接受)。
本题答案不唯一,符合要求的答案均正确
样例输入
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) 我们得到其在坐标系下的坐标
而
因为这里是向下旋转了坐标系.如果向上需要重新计算坐标变化,即将
所以我们只需要对 进行排序即可
由归纳法,斜率最大(最接近 )的点对一定是在相邻的两个点中.
所以只需要按照 的大小进行排序即可
至此问题得到解决
例题
输入描述:
第一行包括三个正整数n,P,Q(5<=n<=106,1<=P,Q<=105)
接下来n行,每行两个正整数x,y表示点的坐标(0<=x,y<=109)
输出描述:
一个有理数P’/Q’表示最接近P/Q的斜率
输入
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; }