BZOJ1527/POI 2005 Point

2 篇文章 0 订阅
2 篇文章 0 订阅

相似点集有一个性质,重心在点集中的位置是相同的,可以通过点集的重心判断现点集是否能还原成原点集.
如果两个点集的点数不同,肯定是不相似的.

  1. 移动:直接根据重心的位置,确定现点集的移动方式,使得两个点集的重心重合.
  2. 缩放:根据两个点集内的点到重心的最短距离确定缩放比例,注意如果最短距离是0要特判.
  3. 旋转:根据每个点到重心的极角给两个点集内的点排序.
    每个点记录两个参数:①到重心的距离,②排完序后与下一个点的夹角大小.记为序列A.
    若两个点集是相似的,它们各自的A序列是循环同构的.那就可以通过最小表示法或者KMP算法判断是否是循环同构,从而确定两者是否相似.
  4. 翻转:将现点集的横坐标全部取反再进行以上判断.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int M=25005;
const double eps=1e-8;
double add[5],pi,mn;
int n,m,flag=-1,tot,pre[M*2];
double tx[M],ty[M];
struct node{
    double a,b,d;
    bool operator<(const node &t)const{
        if(fabs(a-t.a)>eps)return a<t.a;
        return d<t.d;
    }
    bool operator==(const node &t)const{
        return fabs(b-t.b)<=eps&&fabs(d-t.d)<=eps;
    }
    bool operator>(const node &t)const{
        if(fabs(b-t.b)>eps)return b>t.b;
        return d>t.d;
    }
};
struct Point{
    double x,y;
    Point operator-(const Point t)const{
        Point a;
        a.x=x-t.x,a.y=y-t.y;return a;
    }
    Point operator+(const Point t)const{
        Point a;
        a.x=x+t.x,a.y=y+t.y;return a;
    }
    int Q(){
        if(y>=0){
            if(x>=0)return 1;
            return 2;
        }
        if(x>0)return 4;
        return 3;
    }
};
double dis(Point a,Point b){
    Point c=a-b;
    return sqrt(c.x*c.x+c.y*c.y);
}
struct Point_set{
    node s[M*2];//表示点集 
    Point p[M*2],cen;
    void set_cen(){
        cen.x=cen.y=0;
        for(int i=0;i<tot;i++)cen=cen+p[i];
        cen.x/=tot,cen.y/=tot;
    }
    double mndis(){
        double mi=2e9;
        for(int i=0;i<tot;i++){
            s[i].d=dis(p[i],cen);
            if(s[i].d>eps)mi=min(mi,s[i].d);
            else flag=i;
        }
        return mi;
    }
    void get_angle(){
        for(int i=0;i<m;i++){
            Point t=p[i]-cen;
            if(t.x==0){s[i].a=pi/2;}
            else s[i].a=atan(t.y/t.x);
            s[i].a+=add[t.Q()];
            s[i].d=sqrt(t.x*t.x+t.y*t.y);

        }
        sort(s,s+m);
        for(int i=0;i<m;i++){
            if(i<m-1)s[i].b=s[(i+1)%m].a-s[i].a;
            else s[i].b=2*pi-s[i].a+s[0].a;
            s[i+m]=s[i];
        }
    }
}A,B;
void Pre(){
    int i,j,k;
    tot=m;
    pi=atan(1)*4;
    add[1]=0;add[2]=add[3]=pi;add[4]=2*pi;
    A.set_cen();//找到重心 
    mn=A.mndis();
    if(~flag){
        swap(A.p[flag],A.p[m-1]);
        m--;
    }
    A.get_angle();
    pre[0]=-1;
    for(i=1,j=-1;i<m;i++){
        while(j>=0&&!(A.s[j+1]==A.s[i]))j=pre[j];
        if(A.s[j+1]==A.s[i])j++;
        pre[i]=j;
    }
}
bool match(node s1[],node s2[]){//长度都为m 
    int i,j=-1,k=0,len=m*2;
    for(i=0;i<len;i++){
        while(j>=0&&!(s1[j+1]==s2[i]))j=pre[j];
        if(s1[j+1]==s2[i])j++;
        if(j==m-1)return true;
    }
    return false;
}
bool chk(){
    if(m<=1)return true;
    flag=-1;
    B.set_cen();
    double dis=B.mndis();
    double k=mn/dis;
    if(tot!=m&&flag==-1){return false;}
    if(tot==m&&flag!=-1){return false;}
    if(~flag)swap(B.p[flag],B.p[tot-1]);
    Point t=A.cen-B.cen;
    B.cen=B.cen+t;
    for(int i=0;i<m;i++){// x,y * mn/dis
        B.p[i]=B.p[i]+t;
        B.p[i].x=B.cen.x+(B.p[i].x-B.cen.x)*k;
        B.p[i].y=B.cen.y+(B.p[i].y-B.cen.y)*k;
    }
    B.get_angle();//现在就剩匹配了
    if(match(A.s,B.s))return true;
    return false; 
}
void solve(){
    while(n--){
        int k;
        scanf("%d",&k);
        for(int i=0;i<k;i++)scanf("%lf %lf",&B.p[i].x,&B.p[i].y);
        if(k!=tot){puts("NIE");continue;}
        else {
            if(chk()){puts("TAK");continue;}
            for(int i=0;i<tot;i++)B.p[i].x*=(-1);
            if(chk())puts("TAK");
            else puts("NIE");
        }
    }
}
int main(){
    int i,k;
    scanf("%d",&m);tot=m;
    for(i=0;i<m;i++)scanf("%lf %lf",&A.p[i].x,&A.p[i].y);
    Pre();
    scanf("%d",&n);
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值