好久没有打题解了,今天做了一套好题,收获良多。
Description
翔翔最近接到一个任务,要把一个图形做大量的变换操作,翔翔实在是操作得手软,决定写个程序来执行变换操作。
翔翔目前接到的任务是,对一个由n个点组成的图形连续作平移、缩放、旋转变换。相关操作定义如下:
Trans(dx,dy)Trans(dx,dy)Trans(dx,dy) 表示平移图形,即把图形上所有的点的横纵坐标分别加上dx和dy;
Scale(sx,sy)Scale(sx,sy)Scale(sx,sy) 表示缩放图形,即把图形上所有点的横纵坐标分别乘以sx和sy;
Rotate(θ,x0,y0)Rotate(θ,x0,y0)Rotate(θ,x0,y0) 表示旋转图形,即把图形上所有点的坐标绕(x0,y0)顺时针旋转θ角度
由于某些操作会重复运行多次,翔翔还定义了循环指令:
Loop(m)Loop(m)Loop(m)
…
EndEndEnd
表示把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带来的误差。
题解
答案只跟 x,yx,yx,y 的若干次运算有关,我们可以考虑用矩阵乘法(仿射变换)来模拟这个东西。
我们设一个初始矩阵Ans=(xy1)\begin{pmatrix}
x & y & 1\\
\end{pmatrix}(xy1)
首先考虑平移的情况:
要想转移到(x+dxy+dy1)\begin{pmatrix}
x+dx & y+dy & 1\\
\end{pmatrix}(x+dxy+dy1)只需要乘上一个矩阵(100010dxdy1)\begin{pmatrix}
1 & 0& 0\\
0 & 1 &0 \\
dx&dy & 1
\end{pmatrix}⎝⎛10dx01dy001⎠⎞
缩放:
要想转移到(x⋅sxy⋅dy1)\begin{pmatrix}
x\cdot sx & y\cdot dy & 1\\
\end{pmatrix}(x⋅sxy⋅dy1)只需要乘上一个矩阵(sx000sy0001)\begin{pmatrix}
sx& 0& 0\\
0 & sy&0 \\
0&0 & 1
\end{pmatrix}⎝⎛sx000sy0001⎠⎞
旋转就稍微麻烦一些了
首先,我们得知道一个公式,即一个点绕原点逆时针旋转 θ\thetaθ 后的坐标
由于题目要求顺时针,把 θ\thetaθ 取反即可
然后构建出矩阵(cosθsinθ0−sinθcosθ0001)\begin{pmatrix}
cos\theta & sin\theta & 0 \\
-sin\theta & cos\theta & 0 \\
0 & 0 & 1
\end{pmatrix}⎝⎛cosθ−sinθ0sinθcosθ0001⎠⎞因为这是在原点的情况,我们需要先将点平移到原点,旋转,再平移回去,即:
(100010−x0−y01)\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
-x_0 & -y_0 & 1
\end{pmatrix}⎝⎛10−x001−y0001⎠⎞ (cosθsinθ0−sinθcosθ0001)\begin{pmatrix}
cos\theta & sin\theta & 0 \\
-sin\theta & cos\theta & 0 \\
0 & 0 & 1
\end{pmatrix}⎝⎛cosθ−sinθ0sinθcosθ0001⎠⎞ (100010x0y01)\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
x_0 & y_0 & 1
\end{pmatrix}⎝⎛10x001y0001⎠⎞ === (cosθsinθ0−sinθcosθ0x−xcosθ+ysinθy−ycosθ−xsinθ1)\begin{pmatrix}
cos\theta & sin\theta & 0 \\
-sin\theta & cos\theta & 0 \\
x-xcos\theta+ysin\theta & y-ycos\theta-xsin\theta & 1
\end{pmatrix}⎝⎛cosθ−sinθx−xcosθ+ysinθsinθcosθy−ycosθ−xsinθ001⎠⎞
然后遇到循环用一个快速幂优化一下就可以了。
Code
#include<cstdio>
#include<math.h>
#include<cmath>
#include<iostream>
#define pi M_PI
using namespace std;
struct matrix{
double M[4][4];
};
matrix a,a2[1005],ans[105];
int n,top,cnt;
int st[1005];
void empty(matrix &x){
x.M[1][1]=1;x.M[1][2]=0;x.M[1][3]=0;
x.M[2][1]=0;x.M[2][2]=1;x.M[2][3]=0;
x.M[3][1]=0;x.M[3][2]=0;x.M[3][3]=1;
return;
}
void Trans(matrix &x,double xx,double yy){
x.M[1][1]=1;x.M[1][2]=0;x.M[1][3]=0;
x.M[2][1]=0;x.M[2][2]=1;x.M[2][3]=0;
x.M[3][1]=xx;x.M[3][2]=yy;x.M[3][3]=1;
return;
}
void Scale(matrix &x,double xx,double yy){
x.M[1][1]=xx;x.M[1][2]=0;x.M[1][3]=0;
x.M[2][1]=0;x.M[2][2]=yy;x.M[2][3]=0;
x.M[3][1]=0;x.M[3][2]=0;x.M[3][3]=1;
return;
}
void Rotate(matrix &x,double xx,double yy,int zz){
double si=sin(zz/180.0*pi),co=cos(zz/180.0*pi);
x.M[1][1]=co;x.M[1][2]=si;x.M[1][3]=0;
x.M[2][1]=-si;x.M[2][2]=co;x.M[2][3]=0;
x.M[3][1]=xx-xx*co+yy*si;x.M[3][2]=yy-xx*si-yy*co;x.M[3][3]=1;
return;
}
matrix time(matrix x,matrix y){
matrix T;
for (int i=1;i<=3;++i)
for (int j=1;j<=3;++j)
T.M[i][j]=0;
for (int i=1;i<=3;++i)
for (int j=1;j<=3;++j)
for (int k=1;k<=3;++k)
T.M[i][j]+=x.M[i][k]*y.M[k][j];
return T;
}
matrix ksm(matrix A,int b){
matrix R;
empty(R);
for (;b;b>>=1){
if (b&1) R=time(R,A);
A=time(A,A);
}
return R;
}
int main(){
freopen("transform.in","r",stdin);
freopen("transform.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%lf%lf",&ans[i].M[1][1],&ans[i].M[1][2]),ans[i].M[1][3]=1;
st[0]=1;
empty(a2[0]);
int times,jd;
double p,q;
char ch;
while (cin>>ch){
while (ch<'A'||(ch<'a'&&ch>'Z')||ch>'z') ch=getchar();
if (ch=='T'){
while(ch!='(') ch=getchar();
scanf("%lf",&p);ch=getchar();scanf("%lf",&q);ch=getchar();
Trans(a,p,q);
if (top==0){for(int i=1;i<=n;++i)ans[i]=time(ans[i],a);}
else a2[top]=time(a2[top],a);
}
if (ch=='S'){
while(ch!='(') ch=getchar();
scanf("%lf",&p);ch=getchar();scanf("%lf",&q);ch=getchar();
Scale(a,p,q);
if (top==0){for(int i=1;i<=n;++i)ans[i]=time(ans[i],a);}
else a2[top]=time(a2[top],a);
}
if (ch=='R'){
while(ch!='(') ch=getchar();
scanf("%d",&jd);ch=getchar();scanf("%lf",&p);ch=getchar();scanf("%lf",&q);ch=getchar();
jd=-jd;
Rotate(a,p,q,jd);
if (top==0){for(int i=1;i<=n;++i)ans[i]=time(ans[i],a);}
else a2[top]=time(a2[top],a);
}
if (ch=='L'){
while(ch!='(') ch=getchar();
scanf("%d",×);ch=getchar();
if (top==0){
for (int i=1;i<=n;++i)ans[i]=time(ans[i],a2[top]);
empty(a2[top]);
}
st[++top]=times;
empty(a2[top]);
}
if (ch=='E'){
ch=getchar();ch=getchar();
if (top==1){
matrix t=ksm(a2[top],st[top]);
for(int i=1;i<=n;++i)ans[i]=time(ans[i],t);
}
else{
matrix t=ksm(a2[top],st[top]);
a2[top-1]=time(a2[top-1],t);
}
empty(a2[top]);
--top;
}
}
for (int i=1;i<=n;++i)
printf("%.5lf %.5lf\n",ans[i].M[1][1],ans[i].M[1][2]);
return 0;
}
恶心的读入。