1.题目描述:点击打开链接
2.解题思路:本题属于几何变换专题,一开始想着随便枚举两个点,然后都进行一下旋转变换,最后利用原始点和旋转后的点所在直线的中垂线的交点求解。然而发现精度损失很大,而且可能有特殊情况没有考虑到。学习了一下几何变换的方法。
由于旋转操作相当于对一个点构成的矩阵和一个旋转矩阵做乘法运算。最基本的旋转变换就是任意一个点围绕原点进行逆时针旋转。如果改成围绕某个定点(x0,y0)进行旋转可以分解为三步:将被旋转点平移(-x0,-y0),进行基本旋转变换,再平移(x0,y0)。在矩阵中可以理解为三个变换矩阵相乘。本题中,经过n次旋转操作后,得到最终的旋转矩阵ans。通过解一个二元一次方程组即可求得最终的旋转点。
详细的几何变换的讲解请看:点击打开链接 ,本题即该博客的情况2.5的应用。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;
const double eps=1e-8;
const double PI=acos(-1.0);
struct Tran //定义一个结构体,支持输入操作,矩阵相乘操作
{
double x,y,r;
double v[3][3];
Tran()
{
memset(v,0,sizeof(v));
}
void init()
{
scanf("%lf%lf%lf",&x,&y,&r);
memset(v,0,sizeof(v));
v[0][0]=cos(r);v[0][1]=sin(r);
v[1][0]=-sin(r);v[1][1]=cos(r);
v[2][0]=x*(1-cos(r))+y*sin(r);
v[2][1]=y*(1-cos(r))-x*sin(r);
v[2][2]=1;
}
Tran operator*(Tran c)
{
Tran ans;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
ans.v[i][j]+=v[i][k]*c.v[k][j];
return ans;
}
}tran[15];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
Tran ans; //ans矩阵为旋转n次后的矩阵
for(int i=0;i<3;i++)
ans.v[i][i]=1;//设置初始矩阵为单位矩阵
for(int i=0;i<n;i++)
{
tran[i].init();
ans=ans*tran[i];//进行n次相乘
}
double r=atan2(ans.v[0][1],ans.v[0][0]);
if(r<0)r=2*PI+r;
double cosr=ans.v[0][0];
double sinr=ans.v[0][1];
double b2=1-cosr;
double b1=sinr;
double c1=ans.v[2][0];
double c2=ans.v[2][1];
double M=b2*b2+b1*b1;
double x=(b2*c1-b1*c2)/M;//利用行列式求解x,y
double y=(b2*c2+b1*c1)/M;
printf("%.10lf %.10lf %.10lf\n",x,y,r);
}
}