「hdu6631」line symmetric【计算几何】

line symmetric

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 377 Accepted Submission(s): 51

Problem Description

You are given a simple polygon in a two-dimensional plane. Please check whether we can move at most one point such that the simple polygon becomes a line-symmetric simple polygon.

Note that you cannot reorder these points. If you move the i-th point, it still connects to the (i−1)-th point and the (i+1)-th point in the original order. Also, you cannot move a point to a location having an existing point.

Input

The first line contains an integer T T T indicating the number of tests.

Each test begins with one line containing one integer n n n, denoting the number of points in the polygon. Then, the i-th line in the following n n n lines contains two integers x i , y i , ( x i , y i ) x_i,y_i, (x_i,y_i) xi,yi,(xi,yi) is the coordinate of the i i i-th point. For any 1 ≤ i &lt; n 1\leq i&lt; n 1i<n, the i-th point is connected to the ( i + 1 ) (i+1) (i+1)-th point with an edge. Also, the n n n-th point is connected to the first point.

  • 1 ≤ T ≤ 40 1\leq T\leq40 1T40

  • 3 ≤ n ≤ 1000 3\leq n\leq 1000 3n1000

  • coordinates are in the range [ − 1000 , 1000 ] [−1000,1000] [1000,1000]

Output

For each test, if you can move at most one point making the polygon line-symmetric, print a character ′ Y ′ &#x27;Y&#x27; Y in a line, otherwise print a character ′ N ′ &#x27;N&#x27; N in a line. Please note that the final polygon also has to be a simple polygon.

Sample Input

3
3
1 1
1 2
2 2
4
1 1
1 2
2 2
2 1
7
10 0
5 1
6 2
2 3
2 8
11 7
8 5

Sample Output

Y
Y
N

Source

2019 Multi-University Training Contest 5

题意

  • 就是给你一个简单多边形,问是否可以最多移动一个点的位置【相连的点不变】,使得其成为一个轴对称图形【点也要求对称】

题解

  • 显然当 n ≤ 4 n\leq 4 n4时一定有解,然后对于 n &gt; 4 n&gt;4 n>4的情况,如果有解,那么一定存在一堆相邻的点对或者间隔一个点的点对是关于某一条对称轴对称的,所以枚举上述两种情况,去 c h e c k check check使剩下的点对一一对称需要修改的点数是否符合要求就行了
  • 下面这张图是一组比较难想到的情况,答案是 N N N
    在这里插入图片描述
  • 另外附上一组个人认为是比较强的数据,答案是 N N N
    在这里插入图片描述

代码

#pragma GCC optimize(3)
#include<bits/stdc++.h>

using namespace std;
const int maxn=1005;
#define pi acos(-1.0)
const double eps=1e-8;

int n;

int sgn(double k)
{
    return k<-eps?-1:(k<eps?0:1);
}

double Acos(double a)
{
    if(a>=1) return 0;
    else if(a<=-1) return pi;
    return acos(a);
}

struct point{  //点结构体
    double x,y;
    point(double a=0,double b=0) {
        x=a;y=b;
    } 
    point operator+(point other) {
        return point(x+other.x,y+other.y);
    }
    point operator-(point other) {
        return point(x-other.x,y-other.y);
    }
    point operator*(double k) {
        return point(x*k,y*k);
    }
    point operator/(double k) {
        if(k==0) printf("error\n");
        return point(x/k,y/k);
    }
    friend bool operator<(const point &p1,const point &p2) {
        if(sgn(p1.x-p2.x)==0) return p1.y<p2.y;
        return p1.x<p2.x;
    }
    friend bool operator==(const point &p1,const point &p2) {
        return sgn(p1.x-p2.x)==0&&sgn(p1.y-p2.y)==0;
    }
    friend bool operator!=(const point &p1,const point &p2) {
        return sgn(p1.x-p2.x)!=0||sgn(p1.y-p2.y)!=0;
    }
    double operator*(point other) { //点乘,即数量积,内积(ABcos<A,B>)
        return x*other.x+y*other.y;
    }
    double cha(point other) { //叉乘,即向量积,外积(ABsin<A,B>)
        return x*other.y-y*other.x;
    }
    friend double angle(point p1,point p2) {
       return Acos(p1*p2/len(p1)/len(p2));
    }
    bool onseg(point p1,point p2) {//判断q是否在线段p1-p2上
        point q=*this;
        return sgn((p1-q).cha(p2-q))==0&&sgn((p1-q)*(p2-q))<=0;
    }
    friend double dis(point p1,point p2) {     //计算两点距离
        return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
    }
    friend double len2(point p1) {
        return p1.x*p1.x+p1.y*p1.y;
    }
    friend double len(point p1) {
        return sqrt(p1.x*p1.x+p1.y*p1.y);
    }
    friend bool cross(point p1,point p2,point q1,point q2) { //判断线段p1-p2与线段q1-q2有交点,不包括平行和重合
        if(parallel(p1,p2,q1,q2)) return false;
        point inter=intersect(p1,p2,q1,q2);
        if(inter.onseg(p1,p2)&&inter.onseg(q1,q2)) return true;
        return false;
    }

    friend point intersect(point p1,point p2,point q1,point q2) {//计算直线p1-p2是否与直线q1-q2的交点,注意不能平行
        return p1+(p2-p1)*((q2-q1).cha(q1-p1)/(q2-q1).cha(p2-p1));
    }

    friend bool parallel(point p1,point p2,point q1,point q2) { //判断线段p1-p2是否与线段q1-q2平行,重合也会返回true
        return sgn((q2-q1).cha(p2-p1))==0;
    }

    friend bool parallel_intersect(point p1,point p2,point q1,point q2) { //判断线段p1-p2和线段q1-q2在平行的情况下是否重合
        return p1.onseg(q1,q2)||p2.onseg(q1,q2)||q1.onseg(p1,p2)||q2.onseg(p1,p2);
    }

    friend point extend(point p,double len) { //将向量p长度变为len返回一个新的向量,方向不变
        if(sgn(dis(point(0,0),p))==0) return point(0,len);  //0向量
        return point(p*(len/dis(point(0,0),p)));
    }
    friend double dis_point_line(point a,point b,point c) { //点a到直线b,c的距离
        double cos0=(a-b)*(c-b)/(len(a-b)*len(c-b));
        return len(a-b)*sin(Acos(cos0));
    }

    //返回点a在直线p1-p2上的垂足
    friend point chuizu(point a,point p1,point p2) {
        return p1+(p2-p1)*((p2-p1)*(a-p1))/len2(p2-p1);
    } 

    //返回点a关于直线p1-p2的对称点
    friend point duichen(point a,point p1,point p2) {
        point q=chuizu(a,p1,p2);
        return point(2*q.x-a.x,2*q.y-a.y);
    }

    friend point rotate(point p1,point p2,double a) {//点p2绕着点p1顺时针方向旋转a角度,a为弧度,返回新的point
        point vec=p2-p1;
        double xx=vec.x*cos(a)+vec.y*sin(a);
        double yy=vec.y*cos(a)-vec.x*sin(a);
        return point(p1.x+xx,p1.y+yy);
    }
    void print() {
        printf("x:%.3lf  y:%.3lf\n",x,y);
    }
}p[maxn],tmp[maxn];  //tmp表示临时用来判断的数组
typedef point Vector;

int check(int prea,int preb,int a,int b,point p1,point p2)
{
    if(!parallel(tmp[prea],tmp[a],p1,p2)&&!parallel(tmp[preb],tmp[b],p1,p2)) { //特判第一张图的情况
        point k1=intersect(tmp[prea],tmp[a],p1,p2),k2=intersect(tmp[preb],tmp[b],p1,p2);
        if(k1.onseg(tmp[prea],tmp[a])&&k2.onseg(tmp[preb],tmp[b])) {
            return 2;
        }
    }

    if(sgn(dis_point_line(tmp[a],p1,p2)-dis_point_line(tmp[b],p1,p2))==0){  //到对称轴距离相同
        if(sgn((tmp[a]-tmp[b])*(p1-p2))==0) return 0;
        else{
            tmp[b]=duichen(tmp[a],p1,p2);
            return 1;
        }
    }else {
        point duichen_b=duichen(tmp[a],p1,p2);  //看把b改成a关于对称轴对称的情况是否符合
        if(!cross(tmp[prea],tmp[a],tmp[preb],duichen_b)&&duichen_b!=tmp[a]) {
            tmp[b]=duichen_b;
            return 1;
        }
        point duichen_a=duichen(tmp[b],p1,p2);
        if(!cross(tmp[prea],duichen_a,tmp[preb],tmp[b])&&duichen_b!=tmp[b]) {
            tmp[a]=duichen_a;
            return 1;
        }
        return 2; //都不符合返回false
    }
}

bool check1()
{
    for(int i=0;i<n;i++) {
        int j=(i-1+n)%n,k=(i+1)%n;
        point mid=(p[j]+p[k])/2;
        point chui=rotate(mid,p[k],3*pi/2);
        if(!p[i].onseg(mid,chui)) continue;

        int cnt=0;
        for(int j=0;j<n;j++) tmp[j]=p[j];
        for(j=(j-1+n)%n,k=(k+1)%n;;j=(j-1+n)%n,k=(k+1)%n){
            if(k==(j+1)%n) break;
            else if(k==j) {
                if(sgn(dis_point_line(p[k],mid,chui))!=0) cnt++;
                break;
            }
            cnt+=check((j+1)%n,(k-1+n)%n,j,k,mid,chui);
            if(cnt>1) break;
        }
        if(cnt<=1) return true;
    }
    return false;
}



bool check2()
{
    for(int i=0;i<n;i++) {
        int j=i,k=(i+1)%n;
        point mid=(p[j]+p[k])/2,chui=rotate(mid,p[k],3*pi/2);
        int cnt=0;

        for(int j=0;j<n;j++) tmp[j]=p[j];
        for(j=(j-1+n)%n,k=(k+1)%n;;j=(j-1+n)%n,k=(k+1)%n) {
            if(k==(j+1)%n) break;
            else if(k==j) {
                if(sgn(dis_point_line(tmp[k],mid,chui))!=0) cnt++;
                break;
            }
            cnt+=check((j+1)%n,(k-1+n)%n,j,k,mid,chui);
            if(cnt>1) break;
        }
        if(cnt<=1) return true;
    }
    return false;
}

int main()
{
    int t;scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
        if(n<=4) {printf("Y\n");continue;}
        if(check1()) printf("Y\n");
        else if(check2()) printf("Y\n");
        else printf("N\n");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值