CF886F Symmetric Projections

题意:给你平面上n个点,问有多少条过原点的直线,使得这些点在该直线上的投影(做垂直,对应点)形成对称图形?n<=2000。

标程:

 1 #include<bits/stdc++.h>
 2 #define P pair<int,int>
 3 using namespace std;
 4 const int inf=0x3f3f3f3f;
 5 const double eps=1e-8;
 6 const int N=2005;
 7 struct node{double x,y,k;int px,py;}a[N*N];
 8 double mid_x,mid_y;
 9 int n,x[N],y[N],ans,k,t,p[N],die[N],X[N],Y[N],cnt,lst,pos;
10 bool cmp (const node &A,const node &B) {return A.k<B.k;}
11 bool operator == (const node &A,const node &B) {return A.x*B.y==A.y*B.x;}
12 bool check(int x,double b1,double b2)//double不要开成int! 
13 {
14     double dx=mid_y-Y[x],dy=-mid_x+X[x];
15     return fabs(dy*b1-b2*dx)<eps;
16 }
17 int main()
18 {
19     scanf("%d",&n);
20     for (int i=1;i<=n;i++) 
21       scanf("%d%d",&x[i],&y[i]),mid_x+=x[i],mid_y+=y[i]; 
22     mid_x/=n;mid_y/=n;
23     for (int i=1;i<n;i++)
24     if (!die[i])
25       for (int j=i+1;j<=n;j++)
26         if (!die[j]&&fabs(x[i]+x[j]-2*mid_x)<eps&&fabs(y[i]+y[j]-2*mid_y)<eps) {die[i]=die[j]=1;break;}
27     for (int i=1;i<=n;i++) if (!die[i]) X[++t]=x[i],Y[t]=y[i];
28     if (t<2) return puts("-1"),0;
29     for (int i=1;i<t;i++)
30       for (int j=i+1;j<=t;j++)
31       {
32            double dx=mid_y-(Y[i]+Y[j])/2.0,dy=-mid_x+(X[i]+X[j])/2.0;//垂直,斜率负倒数
33            if (dx<0) dx=-dx,dy=-dy; 
34            if (dx==0) dy=fabs(dy);
35            if (dy==0) dx=fabs(dx);//!!!
36            a[++cnt].x=dx,a[cnt].y=dy;a[cnt].px=i;a[cnt].py=j;a[cnt].k=atan2(dy,dx);
37       }
38     sort(a+1,a+cnt+1,cmp);lst=1;
39     for (int i=1;i<=cnt;i++)//check and count 
40       if (i==cnt||!(a[i]==a[i+1]))
41       {
42             if (i-lst+1>=t/2) 
43             {
44                 for (int j=1;j<=t;j++) p[j]=1;
45                 for (;lst<=i;lst++) 
46                   if (p[a[lst].px]&&p[a[lst].py]) p[a[lst].px]=p[a[lst].py]=0;//注意重合的两点不能同时和另一点匹配!一配一 
47                 k=0;
48                 for (int j=1;j<=t;j++) if (p[j]) k++,pos=j;
49                 if (k>(t&1)) continue; 
50                 if (!k||check(pos,a[i].x,a[i].y)) ans++;
51             } 
52             lst=i+1; 
53       }
54     printf("%d
",ans);
55    return 0;
56 }

易错点:1.double不要开成int

2.比较斜率相等的时候,不要比较k关键字,直接用x和y关键字的乘积比较即可。由于最多带小数0.5,所以不会有误差, 直接==即可。

题解:计算几何

一开始的想法是坐标系旋转解旋转角。

正解如下,

找到该图形的重心,易证不管怎么旋转,对称轴一定经过重心。

如果一对点关于重心对称,那么不管怎么旋转这两个点到对称轴的距离都相等,匹配上,删去。

由此发现剩下来的点,n^2枚举两个点作为对称点,有且仅有一条直线满足这两个点的投影在其上关于经过重心的直线对称。

check直线是否符合条件:

如果一条相同斜率的直线出现次数>=n/2,那么说明有足够多的对称点对在其上。看是否能否匹配完所有点,如果n是偶数一定能匹配完,n是奇数会多出一个看是否恰好在对称轴上。

时间复杂度O(n^2)。

原文地址:https://www.cnblogs.com/Scx117/p/9085940.html