计算几何进阶-坐标旋转

坐标旋转主要要找到参考系,对于一个坐标,我们需要维护它与参考系之间的角度及长度比。
坐标旋转公式
对于任意两个不同点A和B,A绕B旋转ang角度的坐标为:
( Δ x c o s ( a n g ) Δ y s i n ( a n g ) + x B , Δ y c o s ( a n g ) + Δ x s i n ( a n g ) + y B )

B - Fractal
题意:操作一次就将该图中的所有线段按照比例变成所给的折线,这样操作d次,问从起点到一个点的长度为总长度的f倍,这个点的坐标是多少。
我们可以以折线的起点到终点的这条线段作为参考系,因为将一条线段变成需要的折线时,我们已知的就是起点和终点的坐标。再维护出每条折线段与参考系的角度及长度比。
计算时我们从后往前计算,维护一个剩余的长度即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-8;
const int maxn=105;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x>0)return 1;
    return -1;
}
struct Point
{
    double x,y;
    double ratio,ang;
    Point(double sx,double sy){x=sx;y=sy;}
    Point(){}
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    Point operator+(const Point &b)const
    {
        return Point(x+b.x,y+b.y);
    }
    Point operator/(const double b)const
    {
        return Point(x/b,y/b);
    }
    Point operator*(const double b)const
    {
        return Point(x*b,y*b);
    }
    double operator^(const Point &b)const 
    {
        return x*b.y-y*b.x;
    }
    double operator*(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
    void out()
    {
        cout<<x<<' '<<y<<endl;
    }
};
Point p[maxn];
typedef Point Vec;
Vec l[maxn];
double dis(Point a)
{
    return sqrt(a.x*a.x+a.y*a.y);
}
double calc(Point a,Point b)
{
    //a.out();
    //b.out();
    double tmp=(a.x*b.x+a.y*b.y)/(dis(a)*dis(b));
    double tmp1= acos(tmp)*((a^b)>0?1:-1);
    //cout<<tmp1<<endl;
    //cout<<acos(1)<<endl;
    //double tmp2= atan2(b.y-a.y,b.x-a.x);
    //cout<<tmp1<<endl;
    //cout<<tmp2<<endl
    return tmp1;
}
Point rotate(Point a,double ang)
{
    Point ans;
    ans.x=a.x*cos(ang)-a.y*sin(ang);
    ans.y=a.y*cos(ang)+a.x*sin(ang);
    return ans;
}
int n,d;
double f;
double L;
double rate;
void go(Point s,double R,int dep,double theta,double ratio)
{
    //cout<<dep<<endl;
    for(int i=1;i<n;i++)
    {
        //cout<<1<<endl;
        double len=dis(l[i])*ratio*R;
        //cout<<L<<' '<<len<<endl;
        if(sgn(L-len)<=0)
        {
            //cout<<1<<endl;
            if(dep)go(s,R/rate,dep-1,theta+l[i].ang,ratio*l[i].ratio);
            else
            {
                s=s+rotate(l[i],theta)/dis(l[i])*L;
                printf("(%.10f,%.10f)\n",s.x,s.y);
            }
            return;
        }
        else L-=len,s=s+rotate(l[i],theta)*ratio;
        //s.out();
    }
}
void solve()
{
    double k=0;
    double len=dis(p[n]-p[1]);
    for(int i=1;i<n;i++)
    {
        l[i]=p[i+1]-p[i];
        double tmpdis=dis(l[i]);
        k+=tmpdis;
        l[i].ratio=tmpdis/len;
        l[i].ang=calc(p[n]-p[1],p[i+1]-p[i]);
    }
    rate=k/dis(p[n]-p[1]);
    double R=1;
    for(int i=1;i<=d;i++)R*=rate;
     L=f*R*k;
    //cout<<L<<endl;
    go(p[1],R,d,0,1);
}
int main()
{
    //freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d",&x,&y);
            p[i]=Point(x,y);
        }
        scanf("%d %lf",&d,&f);
        d--;
        solve();
    }
}

C - Stars
题意:给出星星的坐标,再给出星系的坐标,找出星普图中与星系相似的有多少个,并输出最大亮度的坐标。
首先我们可以枚举星普图中的两个点来对应星系的前两个点,这样就可以依次计算出后面的点,再一层for循环遍历一下在星普图中是否存在这个点。
这样计算的时间复杂度为n^3….,然而还是能过,博主实际想的是由于给的点都是整数点,我们可以依据这个首先可以判断出枚举的两个点是否符合题意,如果符合的话,在查找星普图是否存在这个点时,我们可以将坐标hash,或者放入set里面。这样时间复杂度就会减少了。
然而博主写的还是n^3..

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
const double eps =1e-9;
const double inf=1e9;
const int maxn=1005;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x>0)return 1;
    return -1;
}
struct Point
{
    double x,y;
    double light;
    Point(){}
    Point(double sx,double sy):x(sx),y(sy){}
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    Point operator+(const Point &b)const
    {
        return Point(x+b.x,y+b.y);
    }
    Point operator/(const double b)const
    {
        return Point(x/b,y/b);
    }
    Point operator*(const double b)const
    {
        return Point(x*b,y*b);
    }
    double operator^(const Point &b)const
    {
        return x*b.y-y*b.x;
    }
    double operator*(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
    void out()
    {
        cout<<x<<' '<<y<<endl;
    }
};
Point p[maxn];
Point star[maxn];
char s[45];
int num;
void read()
{
    scanf("%d %s",&num,s);
    int x,y;
    for(int i=1;i<=num;i++)
    {
        scanf("%d %d",&x,&y);
        star[i]=Point(x,y);
    }
}
Point temp[maxn],ans[maxn];
Point rotate(Point a,double ang)
{
    Point res;
    res.x=a.x*cos(ang)-a.y*sin(ang);
    res.y=a.y*cos(ang)+a.x*sin(ang);
    return res;
}
double dis(Point a)
{
    return sqrt(a.x*a.x+a.y*a.y);
}
double calu(Point a,Point b)
{
    return acos((a*b)/(dis(a)*dis(b)))*((a^b)>0?1:-1);
}
int n;
int check(Point a)
{
    for(int i=1;i<=n;i++)
        {
            //p[i].out();
            if(sgn(a.x-p[i].x)==0&&sgn(a.y-p[i].y)==0)return i;
        }
    return -1;
}
bool cmp(Point a,Point b)
{
    if(sgn(a.x-b.x)!=0)return sgn(a.x-b.x)<0;
    return sgn(a.y-b.y)<0;
}
int themaxlight;
Point themaxlightpoint;
bool check2(Point now)
{
    for(int i=1;i<=num;i++)
    {
        if(sgn(star[i].x-now.x)==0&&sgn(star[i].y-now.y)==0)return 1;
    }
    return 0;
}
int solve2()
{
    int res=0;
    int m=num;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)if(i!=j)
        {
            int k;
            for(k=3;k<=m;k++)
            {
                double ang=calu(star[2]-star[1],star[k]-star[1]);
                double ratio=dis(star[k]-star[1])/dis(star[2]-star[1]);
                Point now=star[i]+rotate(star[j]-star[i],ang)*ratio;
                if(!check2(now))
                    break;
            }
            if(k==m+1)
                res++;
        }
    return res;
}
void solve()
{
    double anssum=-1;
    int havenum=0;
    int m=num;
    if(m>n)
    {       
        printf("%s occurs 0 time(s) in the map.\n",s);
        return;
    }
    if(m==1)
    {
        printf("%s occurs %d time(s) in the map.\n",s,n);
        printf("Brightest occurrence: (%d,%d)\n",(int)themaxlightpoint.x,(int)themaxlightpoint.y);
        return;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)if(i!=j)
        {
            int k;
            double sum=p[i].light+p[j].light;
            for(k=3;k<=m;k++)
            {
                double ang=calu(star[2]-star[1],star[k]-star[1]);
                double ratio=dis(star[k]-star[1])/dis(star[2]-star[1]);
                Point now=p[i]+rotate(p[j]-p[i],ang)*ratio;
                //now.out();
                int idx=check(now);
                if(idx!=-1)
                {
                    //cout<<idx<<endl;
                    //cout<<i<<' '<<j<<' '<<idx<<endl;
                    temp[k]=p[idx];
                    sum+=p[idx].light;
                }
                else
                    break;
            }
            if(k==m+1)
            {
                havenum++;
                if(anssum<sum)
                {
                    anssum=sum;
                    ans[1]=p[i],ans[2]=p[j];
                    for(int t=3;t<=m;t++)
                        ans[t]=temp[t];
                }
            }
        }
    if(havenum==0)
    {
        printf("%s occurs 0 time(s) in the map.\n",s);
        return;
    }
    int againnum=solve2();
    printf("%s occurs %d time(s) in the map.\n",s,havenum/againnum);
    sort(ans+1,ans+1+m,cmp);
    printf("Brightest occurrence:");
    for(int i=1;i<=m;i++)
        printf(" (%d,%d)",(int)ans[i].x,(int)ans[i].y);
    puts("");
}
int main()
{
    //freopen("input.txt","r",stdin);
    int cas=1;
    while(~scanf("%d",&n)&&n)
    {
        themaxlight=-1;
        int x,y,light;
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d %d",&x,&y,&light);
            p[i]=Point(x,y);
            p[i].light=light;
            if(light>themaxlight)
            {
                themaxlight=light;
                themaxlightpoint=p[i];
            }
        }
        printf("Map #%d\n",cas++);
        int m;
        scanf("%d",&m);
        while(m--)
        {
            read();
            puts("");
            solve();
        }
        puts("-----");
    }
    return 0;
}

D - Shade of Hallelujah Mountain
题意:在三维坐标系中,给出了一个平面,给出了一个凸多面体,再给出一个点光源。问凸多面体在平面中的阴影面积是多少。
按照hint的想法写就能过了,注意一些细节。特判一下a=0或者b=0的情况。
主要写起来真的让人不想写。。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-9;
const double inf=1e9;
const int maxn=1005;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x>0)return 1;
    return -1;
}
struct Point
{
    double x,y;
    Point(){}
    Point(double sx,double sy):x(sx),y(sy){}
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    Point operator+(const Point &b)const
    {
        return Point(x+b.x,y+b.y);
    }
    Point operator/(const double b)const
    {
        return Point(x/b,y/b);
    }
    double operator^(const Point &b)const
    {
        return x*b.y-y*b.x;
    }
    double operator*(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
    void out()
    {
        cout<<x<<' '<<y<<endl;
    }
};
typedef pair<pair<double,double>,double>P3;
P3 p[maxn];
P3 sp;
double a,b,c,d;
Point p2[maxn];
double dis(Point a)
{
    return sqrt(a.x*a.x+a.y*a.y);
}
bool cmp(Point a,Point b)
{
    double tmp1=atan2(a.y-p2[1].y,a.x-p2[1].x);
    double tmp2=atan2(b.y-p2[1].y,b.x-p2[1].x);
    if(sgn(tmp1-tmp2)!=0)return sgn(tmp1-tmp2)<0;
    return sgn(dis(a-p2[1])-dis(b-p2[1]))<0;
}
Point ans[maxn];
Point ch[maxn];
int n;
double Area(int num)
{
    double res=0;
    for(int i=2;i<num;i++)
    {
        res+=(ch[i]-ch[1])^(ch[i+1]-ch[1])/2;
    }
    return res;
}
void solve3()
{
    for(int i=2;i<=n;i++)
    {
        if(sgn(p2[i].y-p2[1].y<0)||sgn(p2[i].y-p2[1].y)==0&&sgn(p2[i].x-p2[1].x)<0)
            swap(p2[i],p2[1]);
    }
    sort(p2+2,p2+n+1,cmp);
    int anum=0;
    ch[1]=p2[1],ch[2]=p2[2],ch[3]=p2[3];
    int top=3;
    for(int i=4;i<=n;i++)
    {
        while(sgn((p2[i]-ch[top-1])^(ch[top]-ch[top-1]))>=0)top--;
        ch[++top]=p2[i];
    }
    printf("%.2f\n",Area(top));
}
void solve2()
{
    //cout<<sp.second<<endl;
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        //cout<<p[i].second<<endl;
        if(sgn(sp.second-p[i].second)<=0)flag++;
    }
    if(flag==n)
        puts("0.00");
    else if(flag!=0)
        puts("Infi");
    else
    {
        for(int i=1;i<=n;i++)
        {
            double k=p[i].second/(sp.second-p[i].second);
            double x=p[i].first.first-k*(sp.first.first-p[i].first.first);
            double y=p[i].first.second-k*(sp.first.second-p[i].first.second);
            p2[i]=Point(x,y);
            //p2[i].out();
        }
        solve3();
    }
}
double cal(Point a,Point b)
{
    return acos((a*b)/(dis(a)*dis(b)))*((a^b)>0?1:-1);
}
Point rotate(Point a,double ang)
{
    Point res;
    res.x=a.x*cos(ang)-a.y*sin(ang);
    res.y=a.y*cos(ang)+a.x*sin(ang);
    return res;
}
double h[maxn];
double sh;
void init()
{
    double dwn=sqrt(a*a+b*b+c*c);

    for(int i=1;i<=n;i++)
    {
        double up=(p[i].first.first-d/a)*a+(p[i].first.second*b)+p[i].second*c;
        h[i]=up/dwn;

        //cout<<h[i]<<endl;
    }
    double up=(sp.first.first-d/a)*a+(sp.first.second*b)+sp.second*c;
    sh=up/dwn;
}
void solve()
{

    if(sgn(a)!=0||sgn(b)!=0)
    {
        init();
        double ang=cal(Point(a,b),Point(0,sqrt(a*a+b*b)));
        //cout<<ang<<endl;
        //cout<<cos(ang)<<endl;
        //cout<<sin(ang)<<endl;
        for(int i=1;i<=n;i++)
        {
            Point temp;
            temp.x=p[i].first.first;
            temp.y=p[i].first.second;
            temp=rotate(temp,ang);
            p[i].first.first=temp.x,p[i].first.second=temp.y;
        }
        Point temp;
        temp.x=sp.first.first;
        temp.y=sp.first.second;
        temp=rotate(temp,ang);
        sp.first.first=temp.x,sp.first.second=temp.y;
        //cout<<sp.first.first<<' '<<sp.first.second<<' '<<sp.second<<endl;
        ang=cal(Point(sqrt(a*a+b*b),c),Point(0,sqrt(a*a+b*b+c*c)));
        //cout<<ang<<endl;
        for(int i=1;i<=n;i++)
        {
            temp.x=p[i].first.second;
            temp.y=p[i].second;
            temp=rotate(temp,ang);
            p[i].first.second=temp.x,p[i].second=temp.y;
        }
        temp.x=sp.first.second;
        temp.y=sp.second;
        temp=rotate(temp,ang);
        sp.first.second=temp.x,sp.second=temp.y;
        for(int i=1;i<=n;i++)p[i].second=h[i];
        sp.second=sh;
    }

    //for(int i=1;i<=n;i++)
    //{
    //  cout<<p[i].first.first<<' '<<p[i].first.second<<' '<<p[i].second<<endl;
    //}
    solve2();
}
int main()
{
    //freopen("input.txt","r",stdin);
    while(~scanf("%lf %lf %lf %lf",&a,&b,&c,&d))
    {
        //cout<<"********************************"<<endl;
        if(a==0&&b==0&&c==0&&d==0)break;
        scanf("%d",&n);
        double x,y,z;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf %lf %lf",&x,&y,&z);
            p[i].first=make_pair(x,y);
            p[i].second=z;
        }
        double sx,sy,sz;
        scanf("%lf %lf %lf",&sx,&sy,&sz);
        sp.first=make_pair(sx,sy);
        sp.second=sz;
        solve();
    }
    return 0;
}

ALetter to Programmers
题意:现在有3种操作,平移,缩放,旋转,并且还有一个操作”重复”,它可以让它和命令end之间的操作重复x次。现在有n个点,问执行完这些操作后最后的点是多少。
真的好题啊,做的时候没想到怎么做,没想到是我们专业学习到的内容-仿射变换。
仿射变换的矩阵我会在另一篇博客介绍。
知道这个就好做了,只要计算出变换后的矩阵,那么就能一劳永逸了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double PI=acos(-1.0);
const double eps=1e-6;
struct M
{
    int n,m;
    double a[5][5];
    M(int _n=0)
    {
        n=m=_n;
        memset(a,0,sizeof(a));
    }
    M(int _n,int _m)
    {
        n=_n;
        m=_m;
        memset(a,0,sizeof(a));
    }
    void make_I(int _n)
    {
        n=m=_n;
        memset(a,0,sizeof(a));
        for(int i=1; i<=n; i++)a[i][i]=1;
    }
    friend M operator*(M a,M b)
    {
        M c(a.n,b.m);
        for(int k=1; k<=a.m; k++)
            for(int i=1; i<=a.n; i++)
                for(int j=1; j<=b.m; j++)
                    c.a[i][j]+=a.a[i][k]*b.a[k][j];
        return c;
    }
    void out()
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }
};

M Rotate(double x,double y,double z,double d)
{
    M res(4);
    double tmp=(1-cos(d))*x;
    res.a[1][1]=tmp*x+cos(d),res.a[1][2]=tmp*y-sin(d)*z,res.a[1][3]=tmp*z+sin(d)*y;
    tmp=(1-cos(d))*y;
    res.a[2][1]=tmp*x+sin(d)*z,res.a[2][2]=tmp*y+cos(d),res.a[2][3]=tmp*z-sin(d)*x;
    tmp=(1-cos(d))*z;
    res.a[3][1]=tmp*x-sin(d)*y,res.a[3][2]=tmp*y+sin(d)*x,res.a[3][3]=tmp*z+cos(d);
    res.a[4][4]=1;
    return res;
}

M tran(double tx,double ty,double tz)
{
    M res(4);
    for(int i=1; i<=4; i++)res.a[i][i]=1;
    res.a[1][4]=tx,res.a[2][4]=ty,res.a[3][4]=tz;
    return res;
}

M sca(double a,double b,double c)
{
    M res(4);
    res.a[1][1]=a,res.a[2][2]=b,res.a[3][3]=c;
    res.a[4][4]=1;
    return res;
}

M quick_mul(int num,M a)
{
    M res;
    res.make_I(4);
    while(num)
    {
        if(num%2)
            res=res*a;
        num/=2;
        a=a*a;
    }
    return res;
}

M dfs(int num)
{
    char s[20];
    M res;
    res.make_I(4);
    while(~scanf("%s",s))
    {
        if(s[0]=='e')break;
        if(s[0]=='r'&&s[1]=='o')
        {
            double a,b,c,d;
            scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
            d=d/180*PI;
            double dis=sqrt(a*a+b*b+c*c);
            a/=dis,b/=dis,c/=dis;
            M tmp=Rotate(a,b,c,d);
            res=tmp*res;
        }
        else if(s[0]=='r'&&s[1]=='e')
        {
            int times;
            scanf("%d",&times);
            M tmp=dfs(times);
            res=tmp*res;
        }
        else if(s[0]=='t')
        {
            double tx,ty,tz;
            scanf("%lf %lf %lf",&tx,&ty,&tz);
            M tmp=tran(tx,ty,tz);
            res=tmp*res;
        }
        else if(s[0]=='s')
        {
            double a,b,c;
            scanf("%lf %lf %lf",&a,&b,&c);
            M tmp=sca(a,b,c);
            //tmp.out();
            res=tmp*res;
            //res.out();
        }
    }
    M ans=quick_mul(num,res);
    return ans;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int n;
    while(~scanf("%d",&n)&&n)
    {
        M a;
        a.make_I(4);
        M ans=dfs(1);
        //ans.out();
        M p(4,1);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=3; j++)
                scanf("%lf",&p.a[j][1]);
            p.a[4][1]=1;
            M theans;
            theans=ans*p;
            //theans.out();
            printf("%.2f %.2f %.2f\n",theans.a[1][1]+eps,theans.a[2][1]+eps,theans.a[3][1]+eps);
        }
        puts("");
    }
    return 0;
}

总结:坐标旋转的题目都是好题,让人学习到了很多东西。

猜你喜欢

转载自blog.csdn.net/qq_34921856/article/details/80648235
今日推荐