二维向量旋转
知识点参考: http://www.cnblogs.com/woodfish1988/archive/2007/09/10/888439.html
x1= x * cos(θ) - y * sin(θ)
y1= x * sin(θ) + y *cos(θ)
求平面上任意点 绕任意点p(x,y)旋转 角度 sita 后的坐标矩阵
hdu 1700 Points on Cycle
题意:已知圆上一点, 求另外两点, 使得三角形周长最大, 则该三角形为等边三角形, 直接将 一点 顺时和 逆时针 旋转120 得到另外两点, 即可。
代码如下:
using namespace std ; const double pi= acos(-1); #define arc(x) (x / 180 * pi) typedef long long LL ; const double EPS = 0.0005; struct Point { double x,y; Point(){} Point(double x, double y):x(x),y(y){} Point rot( double sita){ return Point( x * cos(sita) - y*sin(sita) , x* sin(sita) + y * cos(sita) ); } }; int main(){ int t; scanf("%d",&t); Point p0, p1, p2; while(t--){ scanf("%lf%lf",&p0.x,&p0.y); p1=p0.rot( arc(120.0) ); p2=p0.rot( arc(-120.0) ); if( p1.y - p2.y > EPS ) swap(p1,p2); else if( (fabs(p1.y - p2.y) < EPS) && (p1.x - p2.x > EPS) ) swap(p1,p2); printf("%.3lf %.3lf %.3lf %.3lf\n",p1.x, p1.y, p2.x, p2.y); } return 0; }
hdu 1033 Edge
题目来源: http://acm.hdu.edu.cn/showproblem.php?pid=1033
分析:求点绕任意点(x,y)旋转角度90(-90)后的坐标。
模板题
double mat[3][3]; // 绕任一点(x,y)旋转角度sita 其中 c = cos(sita) s = sin(sita) void Get_mat(double c, double s, double x, double y){ mat[0][0] = c; mat[0][1] = -s ; mat[0][2] = (1 - c) * x + s * y ; mat[1][0] = s; mat[1][1] = c ; mat[1][2] = (1 - c) * y - s * x; mat[2][0] = mat[2][1] = 0; mat[2][2] = 1; }
代码如下:
using namespace std ; const double pi= acos(-1.0); #define arc(x) (x / 180 * pi) const double EPS = 1e-8; const int Max_N = 35; double mat[3][3]; // 绕过原点和(x,y,z)的轴,转 a 角度的 旋转矩阵,注意传的点向量为单位向量,a为弧度 void Get_mat(double c, double s, double x, double y){ mat[0][0] = c; mat[0][1] = -s ; mat[0][2] = (1 - c) * x + s * y ; mat[1][0] = s; mat[1][1] = c ; mat[1][2] = (1 - c) * y - s * x; mat[2][0] = mat[2][1] = 0; mat[2][2] = 1; } int main(){ string s; int st[3],ed[3],tmp[3]; while(cin>>s){ st[0] = 300, st[1] = 420, st[2] = 1 ; ed[0] = 310, ed[1] = 420, ed[2] = 1 ; int len=s.length(); printf("300 420 moveto\n310 420 lineto\n"); for(int i=0; i<len ;i++){ if(s[i] == 'A') // 顺时针,其实代入的是逆时针 Get_mat(0, 1, ed[0],ed[1]); else // 逆时针,其实代入的是 顺时针 Get_mat(0, -1, ed[0],ed[1]); tmp[0] = mat[0][0]*st[0] + mat[0][1] * st[1] + mat[0][2] * st[2]; tmp[1] = mat[1][0]*st[0] + mat[1][1] * st[1] + mat[1][2] * st[2]; tmp[2] =1; for(int i=0; i<3; i++){ swap(st[i],ed[i]); swap(tmp[i],ed[i]); } printf("%d %d lineto\n",ed[0],ed[1]); } printf("stroke\nshowpage\n"); } return 0; }
三维向量旋转。
左手坐标系下,一点绕任意轴旋转θ角的右乘矩阵:
其中C为cosθ,S为sinθ,A为单位化的旋转轴。
推导过程 请 参照 http://blog.csdn.net/tan625747/article/details/5523728
hdu 2898 旋转
题目来源: http://acm.hdu.edu.cn/showproblem.php?pid=2898
求空间中一个点绕一穿过原点的轴线旋转一定角度后的坐标,沿着旋转轴往原点看旋转的角度为顺时针。
沿着旋转轴往原点看旋转的角度为顺时针,模板
double mat[3][3]; // 绕过原点和(x,y,z)的轴,转 a 角度的 旋转矩阵,注意传的点向量为单位向量,a为弧度 void Get_mat(double x,double y,double z, double a){ double c=cos(a); double s=sin(a); double xy = x*y, xz = x*z, yz = y*z, xx = x*x, yy = y*y , zz = z*z ; mat[0][0] = xx + (1 - xx) * c; mat[0][1] = xy * (1-c) - z * s; mat[0][2] = xz * (1-c) + y * s; mat[1][0] = xy * (1 - c) + z * s; mat[1][1] = yy + (1 - yy) * c; mat[1][2] = yz * (1-c) -x * s ; mat[2][0] = xz * (1 - c) - y * s; mat[2][1] = yz * (1-c) + x * s; mat[2][2] = zz + (1 - zz) * c; }
代码如下:
using namespace std ; const double pi= acos(-1); #define arc(x) (x / 180 * pi) const double EPS = 1e-8; const int Max_N = 35; double mat[3][3]; // 绕过原点和(x,y,z)的轴,转 a 角度的 旋转矩阵,注意传的点向量为单位向量,a为弧度 void Get_mat(double x,double y,double z, double a){ double c=cos(a); double s=sin(a); double xy = x*y, xz = x*z, yz = y*z, xx = x*x, yy = y*y , zz = z*z ; mat[0][0] = xx + (1 - xx) * c; mat[0][1] = xy * (1-c) - z * s; mat[0][2] = xz * (1-c) + y * s; mat[1][0] = xy * (1 - c) + z * s; mat[1][1] = yy + (1 - yy) * c; mat[1][2] = yz * (1-c) -x * s ; mat[2][0] = xz * (1 - c) - y * s; mat[2][1] = yz * (1-c) + x * s; mat[2][2] = zz + (1 - zz) * c; } int main(){ double px[3]; double py[3]; double x,y,z; double a; while(scanf("%lf%lf%lf", &px[0], &px[1], &px[2] ) != EOF){ memset(py,0,sizeof(py)); scanf("%lf%lf%lf%lf",&x,&y, &z, &a); double len = sqrt(x * x + y * y +z *z) ; Get_mat(x / len, y / len, z / len, a); for(int i=0; i<3; i++){ for(int j=0; j<3; j++){ py[i] += mat[i][j]*px[j]; } } printf("%.3lf %.3lf %.3lf\n",py[0],py[1],py[2]); } return 0; }
hit Cubic Rotation
题目来源: http://acm.hrbeu.edu.cn/index.php?act=problem&id=1006&cid=16
The ACM/ICPC Asia Harbin Online First Round Contest 2010
分析: 求 正方体 绕 任意轴 p1p2 向量, 旋转 sita 角度后 , 在 平面 xoy 平面上的 投影面积。 (旋转 + 凸包面积)。
由于是 任意轴 ,故在 代入 旋转公式前, 先平移(-p1)向量, 再 代入旋转公式, 再 逆平移(p1);
注意, 题中 案例 2 ,中的 275 应改成 270, 这题 做了 ,显然 却不能提交。
代码如下:
using namespace std ; const double pi= acos(-1.0); #define arc(x) (x / 180 * pi) const double EPS = 1e-8; double mat[3][3]; // 绕过原点和(x,y,z )的轴,转a 角度的旋转矩阵,注意传入的点向量为单位向量, a 为弧度 void Get_mat(double x, double y, double z, double a){ double c = cos(a); double s = sin(a); double xy = x*y, xz = x*z, yz = y*z, xx = x*x, yy = y*y, zz = z*z; mat[0][0] = xx + (1-xx)* c; mat[0][1] = xy * (1 - c) - z* s; mat[0][2] = xz * (1 - c) + y * s; mat[1][0] = xy * (1 - c) + z * s; mat[1][1] = yy + (1 - yy) *c; mat[1][2] = yz * (1- c) - x * s; mat[2][0] = xz * (1 - c) - y * s; mat[2][1] = yz * (1 - c) + x * s; mat[2][2] = zz + (1 - zz) * c; } int len ; double dir[8][3] = { {0, 0 ,0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 1, 0}, {1, 1, 0}, {1, 1, 1}, {0, 1, 1}}; struct Point{ double x,y,z; Point(){} Point(double x, double y, double z):x(x),y(y),z(z){} Point operator + (Point p){ return Point( x + p.x , y + p.y, z + p.z); } Point operator - (Point p){ return Point( x - p.x , y - p.y, z - p.z); } }; Point cub[10],p1,p2,tmp[10]; // 求凸包以及凸包形成的面积 double add(double a, double b){ if(fabs(a+b) < EPS * (fabs(a) + fabs(b)) ) return 0; return a+b; } struct tubao{ double x,y; tubao(){} tubao(double x, double y):x(x),y(y){} double operator ^(tubao p){ return add(x*p.y ,-y*p.x ); } }; //p0p1 * p0p2 > 0 左转 double xmult(tubao p1, tubao p2, tubao p0){ return add( (p1.x-p0.x)*(p2.y-p0.y), -(p1.y-p0.y)*(p2.x-p0.x) ); } tubao List[10]; int top; tubao qs[10]; bool operator < (tubao a, tubao b){ if(a.y != b.y) return a.y < b.y; else return a.x < b.x; } double graham(int n){ sort(List, List + n); double sum=0; if(n == 0 || n == 1) return 0; qs[0] = List[0]; qs[1] = List[1]; top =1; for(int i=2; i<n; i++){ while(top>= 1 && xmult(qs[top], List[i] , qs[top - 1]) <= 0) top --; qs[++top] = List[i]; } qs[++top] = List[n-2]; int le = top; for(int i=n-3; i>= 0; i--){ while(top >= le && xmult(qs[top], List[i], qs[top - 1]) <= 0) top--; qs[++top] = List[i]; } for(int i=0; i<top ; i++) sum += qs[i] ^ qs[i+1]; sum /= 2; return sum; } int main(){ int t; scanf("%d",&t); while(t--){ int i; double sita; double p[3]; scanf("%d%lf%lf%lf",&len,&p[0],&p[1],&p[2]); scanf("%lf%lf%lf%lf%lf%lf%lf",&p1.x, &p1.y, &p1.z,&p2.x, &p2.y, &p2.z,&sita); for(i=0; i<8; i++){ cub[i].x = p[0] + len*dir[i][0]; cub[i].y = p[1] + len*dir[i][1]; cub[i].z = p[2] + len*dir[i][2]; } //先平移 ( -p1) p2 = p2 - p1; for(i = 0; i<8; i++) cub[i] = cub[i] - p1; // 处理后准备旋转 double unit = sqrt(p2.x * p2.x + p2.y * p2.y + p2.z * p2.z); Get_mat(p2.x / unit, p2.y / unit , p2.z / unit, arc(sita)); for(i=0; i<8 ;i++){ tmp[i].x = mat[0][0]*cub[i].x + mat[0][1]*cub[i].y + mat[0][2] * cub[i].z; tmp[i].y = mat[1][0]*cub[i].x + mat[1][1]*cub[i].y + mat[1][2] * cub[i].z; tmp[i].z = mat[2][0]*cub[i].x + mat[2][1]*cub[i].y + mat[2][2] * cub[i].z; } // 反平移(p1) for(i = 0; i<8; i++) cub[i] = tmp[i] + p1; for(i=0; i<8 ; i++){ List[i].x = cub[i].x; List[i].y = cub[i].y; } printf("%.2lf\n", graham(8)); } return 0; }
2014年携程决赛 飞机的碎片
题目来源: http://acm.hdu.edu.cn/diy/contest_showproblem.php?cid=23086&pid=1002
Problem Description
幸好有卫星把这些碎片拍下来了,可以大致让我们确定一块搜索的区域来确定失事飞机的位置。
为了更好地进行搜救,节约成本,需要在海上划出一块周长尽可能小的区域进行巡逻式的搜救。但是由于技术问题,巡逻的区域只能是矩形。现给出这些碎片的相对位置,请告知这块巡逻区域的周长(请务必保留小数点后6位数字)
由于雷达扫描到的碎片相对于广阔的海洋实在太小,可以用极小的点来代替它。
Input
Output
Sample Input
3 0 0 1 0 0 1
Sample Output
4.000000
Source
分析:
1: 最小包围盒。 题意是求把所有点包含在内的最小矩阵的周长。
2:我们先求所有点构成的凸包, 最小矩阵一定过凸包的某一条边。 如果边 平行于 x轴, 取 最左边点和最右边点 和 y轴方向最远的点, 如果 边 平行于 y 轴, 取 最上和最下的点 和 x轴最远的点, 如果 边 不平行于 轴, 则 对 凸包进行旋转,(平面上绕任意点 进行的旋转矩阵) 使边 平行于 x 轴, 进行同样的处理。求最小值。
代码如下:
const double pi= acos(-1.0); #define arc(x) (x / 180 * pi) const double EPS = 1e-8; const int Max_N = 10005; const double inf = 0xfffffff; // 求凸包以及凸包形成的面积 double add(double a, double b){ if(fabs(a+b) < EPS * (fabs(a) + fabs(b)) ) return 0; return a+b; } struct Point{ double x,y; Point(){} Point(double x, double y):x(x),y(y){} double operator ^(Point p){ return add(x*p.y ,-y*p.x ); } double dist(Point p){ return sqrt( add( (x- p.x)*(x - p.x) ,(y - p.y)*(y - p.y) ) ) ; } }; //p0p1 * p0p2 > 0 左转 double xmult(Point p1, Point p2, Point p0){ return add( (p1.x-p0.x)*(p2.y-p0.y), -(p1.y-p0.y)*(p2.x-p0.x) ); } Point List[Max_N]; int top,n; Point qs[Max_N]; Point tmp[Max_N]; bool operator < (Point a, Point b){ if(a.y != b.y) return a.y < b.y; else return a.x < b.x; } double graham(){ // qs[0] = qs[top] = 原点。 sort(List, List + n); if(n == 0 || n == 1) return 0; qs[0] = List[0]; qs[1] = List[1]; top =1; for(int i=2; i<n; i++){ while(top>= 1 && xmult(qs[top], List[i] , qs[top - 1]) <= 0) top --; qs[++top] = List[i]; } qs[++top] = List[n-2]; int le = top; for(int i=n-3; i>= 0; i--){ while(top >= le && xmult(qs[top], List[i], qs[top - 1]) <= 0) top--; qs[++top] = List[i]; } } // 平面上 绕 任意点 逆时针旋转 角度 sita(弧度) 矩阵 double mat[3][3]; void Get_mat(double x, double y , double sita){ double c = cos(sita); double s = sin(sita); mat[0][0] = c; mat[0][1] = -s; mat[0][2] = (1 - c) * x + s * y ; mat[1][0] = s; mat[1][1] = c; mat[1][2] = (1 - c) * y - s * x; mat[2][0] =mat[2][1] = 0; mat[2][2] = 1; } int main(){ while(scanf("%d",&n) != EOF){ int i,k,j,h; double changy,kuanx; double Min = inf; for(i=0; i<n; i++) scanf("%lf%lf",&List[i].x, &List[i].y); graham(); for(i=0; i<top; i++) // 遍历每一条边 { if(qs[i].x == qs[i+1].x){ for(k=j=0; j<top; j++) // 最大的y k = qs[k].y > qs[j].y ? k : j; changy = qs[k].y - qs[0].y; k=i; for( j = 0; j<top ; j++ ) // 距离最大的x横距 k = fabs(qs[j].x-qs[i].x) > fabs(qs[k].x - qs[i].x) ? j : k; kuanx = fabs( qs[k].x - qs[i].x ) ; Min = min(2*(changy + kuanx), Min ); continue; } else if(qs[i].y == qs[i+1].y){ // 最小的x, 最大的x for(h=k=j=0; j<top ; j++){ k = qs[k].x > qs[j].x ? k : j ; h = qs[h].x < qs[j].x ? h : j ; } kuanx = qs[k].x - qs[h].x ; //距离最大的y 纵距 for(k=j =0 ; j<top; j++) k = fabs(qs[j].y-qs[i].y) > fabs(qs[k].y - qs[i].y) ? j : k; changy = fabs( qs[k].y - qs[i].y ) ; Min = min(2*(changy + kuanx), Min ); continue; } else { double sita = -atan2 ( (qs[i+1].y - qs[i].y) , (qs[i+1].x -qs[i].x ) ); double sss=sita / pi * 180; Get_mat(qs[i].x , qs[i].y , sita); for(j=0; j<top; j++){ if(j != i){ tmp[j].x = mat[0][0] * qs[j].x + mat[0][1] * qs[j].y + mat[0][2]; tmp[j].y = mat[1][0] * qs[j].x + mat[1][1] * qs[j].y + mat[1][2];} else { tmp[j].x = qs[i].x; tmp[j].y = qs[i].y; } } // 最小的x, 最大的x for(h=k=j=0; j<top ; j++){ k = tmp[k].x > tmp[j].x ? k : j ; h = tmp[h].x < tmp[j].x ? h : j ; } kuanx = tmp[k].x - tmp[h].x ; //距离最大的y 纵距 for(k=j =0 ; j<top; j++) k = fabs(tmp[j].y-tmp[i].y) > fabs(tmp[k].y - tmp[i].y) ? j : k; changy = fabs( tmp[k].y - tmp[i].y ) ; Min = min(2*(changy + kuanx), Min ); continue; } } printf("%.6lf\n", Min); } return 0; }