Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。 给定平面上 个整点 ,即横坐标是 到 (包含 和 ) 之间的整数、纵坐标是 到 (包含 和 ) 之间的整数的点。这些点一共确定了 条不同的直线。 给定平面上 个整点 ,即横坐标是 到 (包含 和 ) 之间的整数、纵坐标是 到 (包含 和 ) 之间的整数的点。请问这些点一共确定了多少条不同的直线。
思路详解——第三次最终迭代
我们的代码经过两次迭代后已经发生了巨变:从原来的臃肿不堪,到第二次的整齐有序~~
但是,严格来说,第二次改动只是在一些细节上的改动,对于时间复杂度的优化却只是拘泥于常数。这次修改我们要从根本上对于代码进行复杂度优化。
本次使用了C++的一个宝器——set容器。
本题,我们只需要set容器的以下基本操作:
set<line> lines;
lines.insert(li);//插入一个元素。
lines.size();//求集合的大小
函数set容器内部是基于红黑树的数据结构,因此对于值的查找复杂度是lgn的。并且set不允许含有相同的元素,如果同时插入两个相同的元素集合里还是只会多一个。
//lines.size()=5
//如果li1和li2是同一条直线
lines.insert(li1);
lines.insert(li2);
//lines.size()=6;
这就自动帮助我们完成了去重的步骤。并且,由于插入的复杂度为,而之前的代码要遍历整个vector,复杂度为,因此总体的时间复杂度就从优化为了.
代码
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define pp pop_back
using ll=long long;
using db =double;
int gcd(int a,int b){
if(a<b)return gcd(b,a);
if(b==0)return a;
return gcd(b,a%b);
}
int gcd(int a,int b,int c){
return gcd(gcd(a,b),c);
}
struct line{
int a,b,c;//坚持a是正数
bool operator==(const line &x)const{
return a==x.a&&b==x.b&&c==x.c;
}
bool operator<(const line &x)const{
if(a!=x.a)
return a<x.a;
else if(b!=x.b)
return b<x.b;
else
return c<x.c;
}
};
line getline(int x1,int y1,int x2,int y2){
int a=y2-y1;
int b=x2-x1;
int c=(x2-x1)*y1-(y2-y1)*x1;
int tmp=gcd(abs(a),abs(b),abs(c));
a=a/tmp,b=b/tmp,c=c/tmp;
if(a<0)a=-a,b=-b,c=-c;
return line{a,b,c};
}
set<line> lines;
int main(){
cout<<lines.size()<<' ';
for(int x1=0;x1<=19;x1++){
for(int y1=0;y1<=20;y1++){
for(int x2=0;x2<=19;x2++){
for(int y2=0;y2<=20;y2++){
if(y2-y1==0||x2-x1==0)continue;
auto li=getline(x1,y1,x2,y2);
lines.insert(li);
}
}
}
}
cout<<lines.size()+20+21;
return 0;
}