JZOJsenior3498.【NOIP2013模拟联考14】图形变换(transform)

该博客讨论了如何使用矩阵乘法来高效处理图形的平移、缩放和旋转变换,特别是在面对大量重复操作时。通过构建特定的矩阵,可以避免循环指令的模拟,提高算法效率。博客提供了示例输入输出,并强调了矩阵乘法在解决此类问题中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

problem

Description

翔翔最近接到一个任务,要把一个图形做大量的变换操作,翔翔实在是操作得手软,决定写个程序来执行变换操作。

翔翔目前接到的任务是,对一个由n个点组成的图形连续作平移、缩放、旋转变换。相关操作定义如下:

Trans(dx,dy) 表示平移图形,即把图形上所有的点的横纵坐标分别加上dx和dy;

Scale(sx,sy) 表示缩放图形,即把图形上所有点的横纵坐标分别乘以sx和sy;

Rotate(θ,x0,y0) 表示旋转图形,即把图形上所有点的坐标绕(x0,y0)顺时针旋转θ角度

由于某些操作会重复运行多次,翔翔还定义了循环指令:

Loop(m)

End

表示把Loop和对应End之间的操作循环执行m次,循环可以嵌套。

Input

第一行一个整数n(n<=100)表示图形由n个点组成;

接下来n行,每行空格隔开两个实数xi,yi表示点的坐标;

接下来一直到文件结束,每行一条操作指令。保证指令格式合法,无多余空格。

Output

输出有n行,每行两个空格隔开实数xi,yi表示对应输入的点变换后的坐标。

本题采用Special Judge判断,只要你输出的数值与标准答案误差不能超过1即可。

Sample Input

3

0.5 0

2.5 2

-4.5 1

Trans(1.5,-1)

Loop(2)

Trans(1,1)

Loop(2)

Rotate(90,0,0)

End

Scale(2,3)

End

Sample Output

10.0000 -3.0000

18.0000 15.0000

-10.0000 6.0000

Data Constraint

保证操作中坐标值不会超过double范围,输出不会超过int范围;

指令总共不超过1000行;

对于所有的数据,所有循环指令中m<=1000000;

对于60%的数据,所有循环指令中m<=1000;

对于30%的数据不含嵌套循环。

Hint

【友情提醒】

pi的值最好用系统的值。C++的定义为:#define Pi M_PI

Pascal就是直接为:pi

不要自己定义避免因为pi带来的误差。


analysis

30%的数据用模拟美滋滋地就能水分,但是——

指令总共不超过1000行;对于所有的数据,所有循环指令中m<=1000000

100%数据模拟GG

正解仍旧是矩阵乘法
可以发现,对一个Loop—End循环操作m次,相当于乘上某个和谐的矩阵m
所以就需要矩阵乘法来解决模拟太慢的问题了


构造矩阵式

首先这道 蜜汁 计算几何的题目需要一个旋转公式

a,bθ°
x=(xa)×cosθ(yb)×sinθ+a
y=(xa)×sinθ(yb)×cosθ+b

如何构造矩阵?

A=(x,y,1)(添加无用常数项),分别构造T1,T2,T3使:

A×T1=(x+a,y+b,1)
A×T2=(ax,by,1)
A×T3=((xa)×cosθ(yb)×sinθ+a,(xa)×sinθ(yb)×cosθ+b,1)

我们经过一番和谐拆项以后,得到这三个东东:

T1=1,0,00,1,0a,b,1

T2=a,0,00,b,00,0,1

T3=cosθ,sinθ,0sinθ,cosθ,0aa×cosθ+b×sinθ,ba×sinθb×cosθ,1

得到三个转移矩阵,套上一个栈模拟dfs,就可以了
初始时A=1,0,00,1,00,0,1(零矩阵)每退出一个Loop—End循环,就分三类情况累乘A矩阵
最后把每个点都乘上A矩阵,输出,AC


code

这题好像没怎么卡精度,好评

#include<bits/stdc++.h>
#define Pi 3.14159265358979323846264338327950

using namespace std;

struct matrix
{
    double m[3][3];
};

matrix door[101];
char st[101];
int n;

matrix I=
{
    1,0,0,
    0,1,0,
    0,0,1
};

matrix operator *(matrix a,matrix b)
{
    matrix c;
    for (int i=0;i<=2;i++)
    {
        for (int j=0;j<=2;j++)
        {
            c.m[i][j]=0;
            for (int k=0;k<=2;k++)c.m[i][j]+=a.m[i][k]*b.m[k][j];
        }
    }
    return c;
}

matrix power(matrix a,int k)
{
    matrix ans=I,p=a;
    while (k)
    {
        if (k&1)ans=ans*p;
        p=p*p;
        k/=2;
    }
    return ans;
}

matrix dfs(int t)
{
    matrix a,tmp1,tmp2,tmp3;
    a=tmp1=
    {
        1,0,0,
        0,1,0,
        0,0,1
    };
    tmp2.m[2][2]=1;
    tmp3.m[2][2]=1;
    int now;
    for (scanf("%s",st);*st!='E';scanf("%s",st))
    {
        double theta,x,y;
        switch (*st)
        {
            case 'T':
            {
                sscanf(st,"Trans(%lf,%lf",&tmp1.m[2][0],&tmp1.m[2][1]);
                a=a*tmp1;
            }
            break;
            case 'S':
            {
                sscanf(st,"Scale(%lf,%lf",&tmp2.m[0][0],&tmp2.m[1][1]);
                a=a*tmp2;
            }
            break;
            case 'R':
            {
                sscanf(st,"Rotate(%lf,%lf,%lf",&theta,&x,&y);
                double _sin=sin(-theta/180*Pi),_cos=cos(-theta/180*Pi);
                tmp3=
                {
                    _cos,_sin,0,
                    -_sin,_cos,0,
                    x-x*_cos+y*_sin,y-x*_sin-y*_cos,1
                };
                a=a*tmp3;
            }
            break;
            default:
            {
                sscanf(st,"Loop(%d",&now);
                a=a*dfs(now);
            }
        }
    }
    return power(a,t);
}

int main()
{
    freopen("transform.in","r",stdin);
    freopen("transform.out","w",stdout);
    //freopen("readin.txt","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&door[i].m[0][0],&door[i].m[0][1]);
        door[i].m[0][2]=1;
    }
    matrix a,tmp1,tmp2,tmp3;
    a=tmp1=
    {
        1,0,0,
        0,1,0,
        0,0,1
    };
    tmp2.m[2][2]=1;
    tmp3.m[2][2]=1;
    while (scanf("%s",st)==1)
    {
        double theta,x,y;
        switch (*st)
        {
            case 'T':
            {
                sscanf(st,"Trans(%lf,%lf",&tmp1.m[2][0],&tmp1.m[2][1]);
                a=a*tmp1;
            }
            break;
            case 'S':
            {
                sscanf(st,"Scale(%lf,%lf",&tmp2.m[0][0],&tmp2.m[1][1]);
                a=a*tmp2;
            }
            break;
            case 'R':
            {
                sscanf(st,"Rotate(%lf,%lf,%lf",&theta,&x,&y);
                double _sin=sin(-theta/180*Pi),_cos=cos(-theta/180*Pi);
                tmp3=
                {
                    _cos,_sin,0,
                    -_sin,_cos,0,
                    x-x*_cos+y*_sin,y-x*_sin-y*_cos,1
                };
                a=a*tmp3;
            }   
            break;
            default:
            {
                int x;
                sscanf(st,"Loop(%d",&x);
                a=a*dfs(x);
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        printf("%.4lf %.4lf\n",(door[i]*a).m[0][0],(door[i]*a).m[0][1]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值