题意:
坐标平面有n个点(不与原点(0,0)重复),现考虑一个圆,(0,0)点在圆的边界,问这个圆的边界上最多能有多少其他的点(不含原点)?
我们看一下样例:
4
1 1
0 2
2 0
2 2
如图所示,我们选(0,2)为P,线段op对应的角中,∠PA2O=∠OA3P,说明A2,A3也在圆上,再加上p点,一共是三个,答案就是三
题解:
我一开始是暴力求解,直接枚举两个点,再枚举其他点看在不在边界上,复杂度是O(n3),但显然不行
思路1:
原点肯定在边界,我们可以先枚举一个点p,原点O与p组成线段op,op是圆上的一个弦,再枚举其他点A,根据“同弧所对的圆周角相等”,我们计算出∠OAP,然后找到最多数(众数)即可。但是度数相同不一定在同一个圆上(如图),会关于OP对称,我们只需规定A只能在OP下方,这样就确定位置,即OP(向量) * OA(向量) < 0
时间复杂度O(n2log n)
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef __int128_t LLL;
#define N 2000 + 5
int n, ans = 1, X[N], Y[N];
struct Frac
{
LL fz, fm;
Frac() : Frac(0, 1){
}
Frac(LL fz, LL fm) : fz(fz), fm(fm) {
}
bool operator < (const Frac &rhs)
{
return (LLL) fz * rhs.fm < (LLL) fm * rhs.fz;//判断谁的角大
}
bool operator == (const Frac &rhs)//判断角是否相等
{
return (LLL) fz * rhs.fm == (LLL) fm * rhs.fz;
}
}A[N];
int Cross(int lhs, int rhs)//判断是否平行
{
return X[lhs] * Y[rhs] - X[rhs] * Y[lhs];
}
int Dis2(int lhs, int rhs)//两点的距离的平方和
{
int dx = X[lhs] - X[rhs], dy = Y[lhs] - Y[rhs];
return dx * dx + dy * dy;
}
int Sgn(int x)//用以调整x的正负
{
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
}
Frac GetCosAngle2(int i, int j)
{
int a2 = Dis2(0, i);//求边
int b2 = Dis2(i, j);
int c2 = Dis2(0, j);
int sgn = Sgn(b2 + c2 - a2);
return Frac(1LL * sgn * (b2 + c2 - a2) * (b2 + c2 - a2), 4LL * b2 * c2);//赋值
//余弦定理 cosA=(b2+c2-a2)/2bc
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
scanf("%d%d", X + i, Y + i);
for (int i = 1; i <= n; i ++)
{
int cnt = 0;
for (int j = 1; j <= n; j ++)
if (Cross(i, j) > 0)
A[++ cnt] = GetCosAngle2(i, j);
sort(A + 1, A + cnt + 1);
for (int l = 1, r; l <= cnt; l = r)
{
for (r = l; A[l] == A[r] && r <= cnt; r ++) ;
ans = max(ans, r - l + 1);
}
}
printf("%d\n", ans);
return 0;
}
思路二
任意两个线段的中垂线的交点作圆心,圆肯定过两个线段的四个点,又因为必过原点,所以枚举每一个点,求它与原点所做线段的中垂线,然后求中垂线的所有交点,记录交点数
也就是求每个三角形的外心
特判中垂线都平行的情况
具体求外心的方法:
a(x1,y1) b(x2,y2) c(x3,y3)
外心o(x,y)
外心是垂直平分线的交点,也就是外心到各点距离相等
(x1-x) * (x1-x)-(y1-y) * (y1-y)=(x2-x) * (x2-x)+(y2-y) * (y2-y);
(x2-x) * (x2-x)+(y2-y) * (y2-y)=(x3-x) * (x3-x)+(y3-y) * (y3-y);
化简:
2*(x2-x1)x+2(y2-y1)y=x22+y22-x12-y12;
2*(x3-x2)x+2(y3-y2)y=x32+y32-x22-y22;
A1=2*(x2-x1);
B1=2*(y2-y1);
C1=x22+y22-x12-y12;
A2=2*(x3-x2);
B2=2*(y3-y2);
C2=x32+y32-x22-y22;
所以
A1x+B1y=C1;
A2x+B2y=C2;
结论:
x=((C1B2)-(C2B1))/((A1B2)-(A2B1));
y=((A1C2)-(A2C1))/((A1B2)-(A2B1));
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const ll mod=998244353;
double eqs=1e-6;
struct Point{
double x,y;
Point(){
}
Point(double xx,double yy){
x=xx;
y=yy;
}
}e[maxn];
Point operator+(Point a,Point b){
//向量加
return Point(a.x+b.x,a.y+b.y);
}
Point operator-(Point a,Point b){
//向量减
return Point(a.x-b.x,a.y-b.y);
}
double sqr(double x){
return x*x;
}
double dis(Point a,Point b){
//求ab的长度
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
Point Circum(Point a,Point b,Point c){
//三角形外心
double x1=a.x,y1=a.y;
double x2=b.x,y2=b.y;
double x3=c.x,y3=c.y;
double a1=2*(x2-x1);
double b1=2*(y2-y1);
double c1=x2*x2+y2*y2-x1*x1-y1*y1;//
double a2=2*(x3-x2);
double b2=2*(y3-y2);
double c2=x3*x3+y3*y3-x2*x2-y2*y2;
double x=(c1*b2-c2*b1)/(a1*b2-a2*b1);
double y=(a1*c2-a2*c1)/(a1*b2-a2*b1);
return Point(x,y);
}
map<pair<double,double>,int> m;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf %lf",&e[i].x,&e[i].y);
}
Point o=Point(0,0);
int ans=0;
for(int i=1;i<=n;i++)
{
m.clear();
for(int j=1;j<=n;j++)
{
if(e[i].x*e[j].y-e[j].x*e[i].y<=eqs) continue;//如果平行
Point oo=Circum(o,e[i],e[j]);
ans=max(++m[make_pair(oo.x,oo.y)],ans);
}
}
printf("%d\n",ans+1);
return 0;
}