UVa1497/LA5719 A Letter to Programmers

UVa1497/LA5719 A Letter to Programmers

题目链接

本题是2011年icpc亚洲区预赛北京赛区G

题意

空间里有 n(n≤1 000)个点,你的任务是执行一段程序,输出程序运行结束后所有点的坐标。一共有 5 条指令,如下表所示。

指令 说 明
translate tx ty tz 所有点(x,y,z)移动到(x+tx,y+ty,z+tz)
scale a b c 所有点(x,y,z)移动到(ax,by,cz)
rotate a b c d 所有点旋转。旋转轴是(0,0,0)-(a,b,c),旋转角度是d 度。如果你站在(a,b,c)并且面朝(0,0,0),旋转呈逆时针
repeat k 和 end 配对,二者之间的指令重复执行k 次。k 为32 位带符号整数。注意 repeat 和 end 之间可以嵌套 repeat。
end 和 repeat 指令配对或者作为程序终止

程序不超过100 行,除了n 和k 之外所有参数的绝对值均不超过1 000。

分析

按题意构造平移、缩放、旋转三种变换矩阵(可以参考三维仿射变换矩阵),将所有变换矩阵乘起来得到最终的变换矩阵,然后对每个输入点计算出答案即可。注意处理 repeat 需要用矩阵快速幂计算加速。

一个坑点

题目说输出坐标的结果四舍五入到小数点第二位,我用 io manipulator 来控制小数点,但由于运算过程中三角函数等带来的精度损失,坐标值都加上 1e-6 才 AC 的。

AC 代码

cpp 复制代码
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int n, t; const double e = M_PI/180.; char s[10]; double a[4][4], p[4][4], c[4][4], r[52][4][4];

void identity_mat(double (&t)[4][4]) {
    for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) t[i][j] = i==j ? 1. : 0.;
}

void trans_mat(double tx, double ty, double tz, double (&t)[4][4]) {
    identity_mat(t); t[0][3] = tx; t[1][3] = ty; t[2][3] = tz;
}

void scale_mat(double a, double b, double c, double (&t)[4][4]) {
    identity_mat(t); t[0][0] = a; t[1][1] = b; t[2][2] = c;
}

void rot_mat(double a, double b, double c, double d, double (&t)[4][4]) {
    double s = sqrt(a*a + b*b + c*c); a /= s; b /= s; c /= s; d *= e;
    double co = cos(d), si = sin(d), c1 = 1.-co; identity_mat(t);
    t[0][0] = co + a*a*c1; t[0][1] = a*b*c1 - c*si; t[0][2] = a*c*c1 + b*si;
    t[1][0] = a*b*c1 + c*si; t[1][1] = co + b*b*c1; t[1][2] = b*c*c1 - a*si;
    t[2][0] = a*c*c1 - b*si; t[2][1] = b*c*c1 + a*si; t[2][2] = co + c*c*c1;
}

void copy(const double (&a)[4][4], double (&b)[4][4]) {
    for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) b[i][j] = a[i][j];
}

void mat_mul(const double (&a)[4][4], const double (&b)[4][4], double (&c)[4][4]) {
    for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) {
        c[i][j] = 0.;
        for (int k=0; k<4; ++k) c[i][j] += a[i][k]*b[k][j];
    }
}

void mat_pow(const double (&x)[4][4], int y, double (&b)[4][4]) {
    identity_mat(b); copy(x, p);
    while (y) {
        if (y & 1) mat_mul(b, p, a), copy(a, b);
        if (y >>= 1) mat_mul(p, p, a), copy(a, p);
    }
}

void dfs(int k = -1) {
    while (true) {
        cin >> s;
        if (s[0] == 'r' && s[1] == 'e') {
            int k1; cin >> k1; identity_mat(r[++t]); dfs(k1);
        } else if (s[0] == 'e') {
            if (k >= 0) mat_pow(r[t], k, c), mat_mul(c, r[--t], a), copy(a, r[t]);
            return;
        } else {
            if (s[0] == 't') {
                double x, y, z; cin >> x >> y >> z; trans_mat(x, y, z, c);
            } else if (s[0] == 's') {
                double x, y, z; cin >> x >> y >> z; scale_mat(x, y, z, c);
            } else {
                double x, y, z, d; cin >> x >> y >> z >> d; rot_mat(x, y, z, d, c);
            }
            mat_mul(c, r[t], a); copy(a, r[t]);
        }
    }
}

void solve() {
    identity_mat(r[t=0]); dfs();
    while (n--) {
        double x, y, z; cin >> x >> y >> z;
        cout << r[0][0][0]*x + r[0][0][1]*y + r[0][0][2]*z + r[0][0][3] + 1e-6 << ' ' << 
            r[0][1][0]*x + r[0][1][1]*y + r[0][1][2]*z + r[0][1][3] + 1e-6 << ' ' << 
            r[0][2][0]*x + r[0][2][1]*y + r[0][2][2]*z + r[0][2][3] + 1e-6 << endl;
    }
    cout << endl;
}

int main() {
    cout << fixed << setprecision(2);
    while (cin >> n && n) solve();
    return 0;
}
相关推荐
图灵信徒15 天前
2024南京icpc区域赛详解与难点解释
c++·acm·icpc·算法竞赛
西望云天17 天前
The 2024 ICPC Asia Nanjing Regional Contest(2024南京区域赛EJKBG)
数据结构·算法·icpc
西望云天17 天前
The 2023 ICPC Asia Shenyang Regional Contest(2023沈阳区域赛CEJK)
数据结构·算法·icpc
stolentime24 天前
二维凸包——Andrew 算法学习笔记
c++·笔记·学习·算法·计算几何·凸包
西望云天1 个月前
基础组合计数(三道例题)
数据结构·算法·icpc
西望云天1 个月前
Trie树实战:三道典型例题
数据结构·算法·icpc
惆怅客1232 个月前
UVa12389/LA5821 Cybercrime Donut Investigation
树状数组·icpc·uva·k-d tree
惆怅客1233 个月前
UVa1480/LA5034 Jewel
线段树·icpc·uva·动态开点·“前缀”思想
惆怅客1234 个月前
UVa12298 3KP-BASH Project
模拟·icpc·uva