#include <iostream>
#include <stdio.h>
#include <map>
#include <vector>
using namespace std;
/*
问题:在二维平面上,有一些点,请找出经过点数最多的那条线。
分析:暴力破解,共有n(n-1)/2 条直线,对每个直线,判断剩余n-2个点是不是在直线上
总时间复杂度为O(n^3)。
可能需要排除某些线段,不是所有线段。
或者是求闭包,闭包问题记得需要3个顶点,然后每次替换掉其中两个顶点,画出图形
然后得到闭包中以某个点作为直线其中一点的斜率最大值和最小值,遍历斜率吗?
不会。
书上解法:
牛逼,画出所有所有直线,统计所有直线出现次数,时间复杂度为O(n^2)
之所以降低了复杂度,将处理的对象从点变成了线。
易错:斜率为浮点数,作为映射中的键的时候,需要考虑与斜率k,在精度p范围之内的
k-p,k+p的斜率对应直线都是一样的,统计斜率为k的直线的时候,需要将k-p,k+p对应的直线也进行统计
例如:k=1.12,p=0.01,那么斜率1.11,1.13对应的直线也需要统计处理
输入:
6(点的个数,接下来有n行,每一行对应一个点,每个点第一个数值为x,第二个数值为y)
1 1
2 2
3 3
1 2
2 4
3 1
输出:
1(该直线的斜率) 0(该直线的截距)
关键:
1 牛逼,画出所有所有直线,统计所有直线出现次数,时间复杂度为O(n^2)
之所以降低了复杂度,将处理的对象从点变成了线。
易错:斜率为浮点数,作为映射中的键的时候,需要考虑与斜率k,在精度p范围之内的
k-p,k+p的斜率对应直线都是一样的,统计斜率为k的直线的时候,需要将k-p,k+p对应的直线也进行统计
例如:k=1.12,p=0.01,那么斜率1.11,1.13对应的直线也需要统计处理
还要考虑斜率无穷大的情况
2
//直线存放好后,下面就立即统计该直线出现次数,如果后续再次出现相同直线,肯定已经累加了统计次数,这样做是对的
int times = count(slopeToLines , intersectToTimes , line);
*/
const double g_precision = 1e-3;//给定精度,用于判定两个浮点数是否相同
typedef struct Point
{
Point(){}
double _x;
double _y;
}Point;
class Line
{
public:
Line(){}
Line(Point& point1 , Point& point2):_point1(point1),_point2(point2),_isSlopeInfinite(false){}//默认斜率不是无限大的
//根据两点,计算斜率和截距
void calculate()
{
//如果两个点横坐标不等
if( fabs(_point1._x - _point2._x) > g_precision )
{
_slope = (_point1._y - _point2._y) / (_point1._x - _point2._x);
_intersect = _point1._y - _slope * _point1._x;
_slope = standardlize(_slope);
_intersect = standardlize(_intersect);
}
//设置斜率无限大为真
else
{
_isSlopeInfinite = true;
_intersect = standardlize(_point1._x);//设置截距为距离x轴的距离
}
}
//将斜率和截距浮点数精度控制在设置的精度范围,采用的方法是:将浮点数除以精度取整后再乘以精度
double standardlize(double value)
{
int num = (int) (value / g_precision);
return g_precision * num;
}
//判断两个直线是否相等:斜率和截距都要在相等或者都是斜率无穷大且斜率相等,即两直线的斜率和距离差值都要在精度范围之内
bool isEqual(Line& line)
{
//由于直线中的斜率和截距都要计算,因此先执行计算函数
calculate();
line.calculate();
//如果两个的斜率都不是无穷大
if(!line._isSlopeInfinite && (!_isSlopeInfinite) )
{
if( fabs(_slope - line._slope) <= g_precision && fabs(_intersect - line._intersect) <= g_precision )
{
return true;
}
else
{
return false;
}
}
//如果两个斜率都是无穷大
else if(line._isSlopeInfinite && _isSlopeInfinite)
{
if( fabs(_intersect - line._intersect))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
Point _point1;
Point _point2;
double _slope;
double _intersect;
bool _isSlopeInfinite;//斜率是否无穷大,即给定两个点的x坐标相同
};
//统计与line相等的直线个数
int countEquivalentLine(vector<Line>& lines , Line& line)
{
if(lines.empty() )
{
return 0;
}
int count = 0;
for(vector<Line>::iterator it = lines.begin() ; it != lines.end() ; it++)
{
if(line.isEqual(*it))
{
count++;
}
}
return count;
}
int count(map< double , vector<Line> >& slopeToLines , map<double , int>& intersectToTimes , Line& line)
{
//如果斜率正常,进入斜率映射中进行统计
if(!line._isSlopeInfinite)
{
int totalCount = 0;
if(slopeToLines.find(line._slope) != slopeToLines.end())
{
totalCount += countEquivalentLine((slopeToLines.find(line._slope))->second , line );
}
//因为考虑到精度,因此斜率k+p,k-p默认与斜率为k的直线是相同的
if( slopeToLines.find(line._slope + g_precision) != slopeToLines.end())
{
totalCount += countEquivalentLine((slopeToLines.find(line._slope+ g_precision))->second , line );
}
if( slopeToLines.find(line._slope - g_precision) != slopeToLines.end())
{
totalCount += countEquivalentLine((slopeToLines.find(line._slope - g_precision))->second , line );
}
return totalCount;
}
//首先判定该直线斜率是否无穷大, 如果无穷大,进入无穷大映射中查找截距相同的直线即为相同直线
else
{
int totalCount = 0;
if( intersectToTimes.find(line._intersect) != intersectToTimes.end() )
{
totalCount += intersectToTimes.find(line._intersect)->second;
}
if( intersectToTimes.find(line._intersect + g_precision) != intersectToTimes.end() )
{
totalCount += intersectToTimes.find(line._intersect + g_precision)->second;
}
if( intersectToTimes.find(line._intersect - g_precision) != intersectToTimes.end() )
{
totalCount += intersectToTimes.find(line._intersect - g_precision)->second;
}
return totalCount;
}
}
Line findLine(vector<Point>& vecPoint)
{
if(vecPoint.empty())
{
return Line();
}
int size = vecPoint.size();
// 将斜率无限大(即与x轴垂直)的直线单独放在一个映射中,键是截距(距离x轴的距离),值是该截距对应直线出现次数
map<double , int> intersectToTimes;
map< double , vector<Line> > slopeToLines;
int maxTimes = 0;
Line maxLine;
for(int i = 0 ; i < size - 1; i++)
{
Point point1 = vecPoint.at(i);
for(int j = i + 1 ; j < size ; j++)
{
Point point2 = vecPoint.at(j);
Line line(point1 , point2);
line.calculate();
//如果斜率无限大,放入斜率无限大的直线数组中
if( line._isSlopeInfinite)
{
map<double ,int>::iterator it = intersectToTimes.find(line._intersect);
if( it != intersectToTimes.end())
{
(it->second)++;
}
else
{
intersectToTimes.insert(pair<double , int>(line._intersect , 1));
}
}
//斜率正常的直线,存入映射中
else
{
//如果斜率存在,就加入到该斜率对应的直线数组中
map< double , vector<Line> >::iterator it = slopeToLines.find(line._slope);
if( it != slopeToLines.end() )
{
it->second.push_back(line);
}
//如果该斜率不存在,就建立映射
else
{
vector<Line> lines;
lines.push_back(line);
slopeToLines.insert(pair< double , vector<Line> >(line._slope , lines));
}
}
//直线存放好后,下面就立即统计该直线出现次数,如果后续再次出现相同直线,肯定已经累加了统计次数,这样做是对的
int times = count(slopeToLines , intersectToTimes , line);
if(times > maxTimes)
{
maxTimes = times;
maxLine = line;
}
}
}
return maxLine;
}
void process()
{
int n;
vector<Point> vecPoint;
Line maxLine;
while(cin >> n)
{
vecPoint.clear();
for(int i = 0 ; i < n ; i++)
{
Point point;
cin >> point._x >> point._y;
vecPoint.push_back(point);
}
//所有的点输入好之后,下面就是计算包含点最多的直线
maxLine = findLine(vecPoint);
cout << maxLine._slope << " " << maxLine._intersect << endl;
}
}
int main(int argc, char* argv[])
{
process();
getchar();
return 0;
}
程序员面试金典: 9.7数学与概率 7.6在二维平面上,有一些点,请找出经过点最多的那条线
最新推荐文章于 2022-05-28 01:35:33 发布