V1:循环方式
- 按照题意一步步实现,命名为step1至6方便对照。
- 用方向变量记录填充路线,碰到边界后转向。
- 计算量最大的是逆变换的4层循环。中途不变的表达式提取到外层,但影响不大。
print
设计为函数模板,输出整型、浮点型矩阵便于调试。- 评测结果:
正确 100 15ms 4.312MB
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
constexpr int K = 8;
int Q[K][K];
int M[K][K];
double P[K][K];
template <typename T>
void print(const T a[K][K]) {
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
cout << a[i][j] << " \n"[j == 7];
}
}
}
int clamp(int x, int lo, int hi) { return (x < lo ? lo : (x > hi ? hi : x)); }
void step1() { // 读入量化矩阵
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
cin >> Q[i][j];
}
}
}
void step3(int n) { // 读入扫描数据,折返填充
for(int r = 0, c = 0, dir = 'U'; n > 0 && cin >> M[r][c]; --n) {
if(dir == 'U') { // 斜向左上
--r;
if(r < 0) {
++r;
++c;
dir = 'D';
} else {
++c;
if(c >= K) {
r += 2;
--c;
dir = 'D';
} else {
}
}
} else {
assert(dir == 'D');
++r;
if(r >= K) {
--r;
++c;
dir = 'U';
} else {
--c;
if(c < 0) {
++c;
dir = 'U';
}
}
}
}
}
void step4() {
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
M[i][j] *= Q[i][j];
}
}
}
void step5() {
const double r2 = sqrt(0.5);
const double p8 = acos(-1) / K;
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
for(int u = 0; u < K; ++u) {
const double a = (u == 0 ? r2 : 1);
const double c1 = cos(p8 * (i + 0.5) * u);
for(int v = 0; v < K; ++v) {
const double b = (v == 0 ? r2 : 1);
const double c2 = cos(p8 * (j + 0.5) * v);
P[i][j] += a * b * M[u][v] * c1 * c2;
}
}
P[i][j] /= 4;
}
}
}
void step6() {
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
M[i][j] = clamp(P[i][j] + 128.5, 0, 255);
}
}
}
int main() {
step1();
// step2: 全局数据自动初始化。
int n, T; // 扫描数据的个数,任务(步骤)
cin >> n >> T;
step3(n);
if(T >= 1) {
step4();
}
if(T >= 2) {
step5();
step6();
}
print(M);
return 0;
}
V2:函数式写法
V1版有多处类似的双层8x8循环,用函数式风格简化:
- 把双层循环抽象成映射。
- 将内层动作抽象为多个函数。用lambda表达式也可以。
- 用索引数组实现折返填充。
整体约缩减一半代码量,但不如V1版直观。
#include <bits/stdc++.h>
using namespace std;
constexpr int K = 8;
const int IDX[] = {0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43,
36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
int n, T, Q[K][K], M[K][K]; // 扫描数据的个数,任务(步骤)
double P[K][K];
const double r2 = sqrt(0.5);
const double p8 = acos(-1) / 8; // 八分之Pi
template<typename F>
void loop(F f) { // 将函数f映射到8x8矩阵网格上
for(int i = 0; i < K; ++i) {
for(int j = 0; j < K; ++j) {
f(i, j);
}
}
}
int clamp(int x, int l, int h) { return x < l ? l : (x > h ? h : x); }
double alpha(int u) { return u == 0 ? r2 : 1; }
double cs(int i, int u) { return cos(p8 * (i + 0.5) * u); }
void load(int i, int j) { cin >> Q[i][j]; }
void scan(int i, int j) { if(n-- > 0) { cin >> M[0][IDX[i * 8 + j]]; } }
void mult(int i, int j) { M[i][j] *= Q[i][j]; }
void bias(int i, int j) { M[i][j] = clamp(P[i][j] + 128.5, 0, 255); }
void dump(int i, int j) { cout << M[i][j] << " \n"[j == 7]; }
void dct(int i, int j) {
double& e = P[i][j];
auto converter = [&e, i, j](int u, int v) {
e += alpha(u) * alpha(v) * M[u][v] * cs(i, u) * cs(j, v);
};
loop(converter);
e /= 4;
}
int main() {
loop(load); // step 1。全局数据自动初始化,step2略。
cin >> n >> T;
loop(scan);
if(T >= 1) {
loop(mult); // step 4
}
if(T >= 2) {
loop(dct); // step 5
loop(bias); // step 6
}
loop(dump);
return 0;
}