问题引入
给出平面个点 , 求出任意两点组成的直线中斜率最大的两个点.
分析
朴素的思路 : 枚举任意两个点 , 计算斜率最大的点对. 时间复杂度. 在比赛中往往不可行.
但是可以证明的是如果我们安装 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;
}