【备战蓝桥杯】12.直线(3)——std::set时间复杂度优化

343 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。 给定平面上 2×32 × 3 个整点 {(x,y)0x<2,0y<3,xZ,yZ}\{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z\},即横坐标是 0011 (包含 0011) 之间的整数、纵坐标是 0022 (包含 0022) 之间的整数的点。这些点一共确定了 1111 条不同的直线。 给定平面上 20×2120 × 21 个整点 {(x,y)0x<20,0y<21,xZ,yZ}\{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z\},即横坐标是 001919 (包含 001919) 之间的整数、纵坐标是 002020 (包含 002020) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

思路详解——第三次最终迭代

我们的代码经过两次迭代后已经发生了巨变:从原来的臃肿不堪,到第二次的整齐有序~~

但是,严格来说,第二次改动只是在一些细节上的改动,对于时间复杂度的优化却只是拘泥于常数。这次修改我们要从根本上对于代码进行复杂度优化。

本次使用了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;

这就自动帮助我们完成了去重的步骤。并且,由于插入的复杂度为O(lgn)O(lgn),而之前的代码要遍历整个vector,复杂度为O(n)O(n),因此总体的时间复杂度就从O(n3)O(n^3)优化为了O(n2lgn)O(n^2lgn).

代码

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