- C++
GESP编程题详解(图像压缩与打印数字)
- 2025-7-13 18:55:02 @
C++学习笔记教程:GESP编程题详解(图像压缩与打印数字)
一、4012:【GESP2306四级】图像压缩
1. 题目理解
核心需求:将256级灰阶(0-255,十六进制00-FF)的灰度图像压缩为16级灰阶(0-F),具体规则如下:
- 统计所有灰阶的出现次数,取前16种(次数多的优先,次数相同则灰阶值小的优先);
- 前16种灰阶直接映射为0-F(次数最多的为0,依次类推);
- 其他灰阶转换为“最近”的16种之一(绝对值差最小,差相同则选编号小的)。
2. 解题思路
- 数据准备:用结构体存储灰阶值及其出现次数;
- 统计频率:解析输入的十六进制像素,转换为十进制后统计每种灰阶的出现次数;
- 筛选前16种:按规则排序灰阶,选取前16种作为保留灰阶;
- 转换输出:
- 第一行输出16种保留灰阶的十六进制编码(共32个字符);
- 后续行输出压缩后的图像,每个像素用0-F表示。
3. 完整代码与详细注释
#include<iostream>
#include<algorithm> // 包含排序函数sort
using namespace std;
// 结构体:存储灰阶信息(bm为灰阶值,cs为出现次数)
struct huiJie {
int bm = 0; // 灰阶值(十进制0-255)
int cs = 0; // 出现次数(初始为0)
};
huiJie huiJies[300]; // 存储256种灰阶的数组(开300确保足够)
string words[30]; // 存储输入的图像行(每行是十六进制字符串)
// 初始化灰阶数组:为0-255所有灰阶设置初始值
void huiJieInit() {
for (int i = 0; i <= 255; i++) {
huiJies[i].bm = i; // 灰阶值从0到255依次赋值
}
}
// 统计指定灰阶的出现次数
void CntHuiJie(int t) {
for (int i = 0; i <= 255; i++) {
if (huiJies[i].bm == t) { // 找到目标灰阶
huiJies[i].cs += 1; // 次数+1
break; // 找到后退出循环,提高效率
}
}
}
// 十六进制转十进制:将两个字符(如"CF")转换为十进制数
int hexTo10(char t1, char t2) {
int res = 0;
// 处理低位字符(右边的字符,如"CF"中的'F')
if (t2 >= 'A') { // 若为字母(A-F)
res = t2 - 'A' + 10; // A→10,B→11,…,F→15
} else { // 若为数字(0-9)
res = t2 - '0';
}
// 处理高位字符(左边的字符,如"CF"中的'C'),并乘以16(十六进制进位)
if (t1 >= 'A') {
res += (t1 - 'A' + 10) * 16;
} else {
res += (t1 - '0') * 16;
}
return res; // 例如"CF"→12*16+15=207
}
// 十进制转十六进制:将十进制数转换为两位十六进制字符串(如160→"A0")
string decTo16(int n) {
string str = "";
// 特殊情况:n=0时直接返回"00"(避免空字符串)
if (n == 0) {
return "00";
}
while (n != 0) {
int t = n % 16; // 取余得到低位(0-15)
if (t >= 10) {
str += char(t - 10 + 'A'); // 10-15→A-F
} else {
str += char(t + '0'); // 0-9→0-9
}
n /= 16; // 处理高位
}
// 若结果长度为1,补前导0(确保每个灰阶用2个字符表示)
if (str.size() == 1) {
str = "0" + str; // 例如3→"03"
}
reverse(str.begin(), str.end()); // 反转得到正确顺序(例如计算时先得低位,反转后高位在前)
return str;
}
// 排序规则:用于sort函数,先按出现次数降序,次数相同按灰阶值升序
bool cmp(huiJie a, huiJie b) {
if (a.cs != b.cs) {
return a.cs > b.cs; // 次数多的排在前
} else {
return a.bm < b.bm; // 次数相同,灰阶值小的排在前
}
}
int main() {
huiJieInit(); // 初始化灰阶数组(设置0-255的灰阶值)
int n; // 图像的行数
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> words[i]; // 读取每行的十六进制像素字符串
}
// 第一步:统计所有灰阶的出现次数
int len = words[1].size(); // 每行的字符长度(确保所有行长度相同)
for (int i = 1; i <= n; i++) { // 遍历每行
for (int j = 0; j < len; j += 2) { // 每2个字符为一个像素
char t1 = words[i][j]; // 高位字符
char t2 = words[i][j + 1]; // 低位字符
int gray = hexTo10(t1, t2); // 转换为十进制灰阶值
CntHuiJie(gray); // 统计该灰阶的出现次数
}
}
// 第二步:排序灰阶,筛选出前16种
sort(huiJies, huiJies + 256, cmp); // 对0-255的灰阶按规则排序
// 第三步:输出16种保留灰阶的十六进制编码(共32个字符)
for (int i = 0; i <= 15; i++) {
cout << decTo16(huiJies[i].bm);
}
cout << endl;
// 第四步:转换并输出压缩后的图像
for (int i = 1; i <= n; i++) { // 遍历每行
for (int j = 0; j < len; j += 2) { // 遍历每个像素
char t1 = words[i][j];
char t2 = words[i][j + 1];
int gray = hexTo10(t1, t2); // 原灰阶的十进制值
bool isInTop16 = true; // 标记是否属于前16种灰阶
for (int p = 0; p <= 15; p++) {
if (gray == huiJies[p].bm) { // 属于前16种
isInTop16 = false;
// 输出对应的编号(0-F)
if (p >= 10) {
cout << char(p - 10 + 'A'); // 10-15→A-F
} else {
cout << char(p + '0'); // 0-9→0-9
}
break;
}
}
// 若不属于前16种,找最近的保留灰阶
if (isInTop16) {
int minDiff = 1000; // 最小差值(初始设为较大值)
int bestP = 0; // 最佳匹配的编号
for (int p = 0; p <= 15; p++) {
int diff = abs(gray - huiJies[p].bm); // 计算绝对值差
if (diff < minDiff) { // 找到更小的差值
minDiff = diff;
bestP = p;
}
}
// 输出最佳匹配的编号
if (bestP >= 10) {
cout << char(bestP - 10 + 'A');
} else {
cout << char(bestP + '0');
}
}
}
cout << endl; // 每行结束换行
}
return 0;
}
4. 关键知识点解析
- 结构体(struct):
huiJie
结构体封装了灰阶值(bm
)和出现次数(cs
),便于批量管理数据; - 进制转换:
hexTo10
:将两位十六进制字符转为十进制(如"FF"→255);decTo16
:将十进制转为两位十六进制字符串(如255→"FF");
- 排序(sort):结合自定义比较函数
cmp
,实现按“次数降序+值升序”的复杂排序; - 查找逻辑:通过循环判断灰阶是否属于前16种,或寻找最小差值的灰阶,体现了线性查找的应用。
二、4094:【GESP2412三级】打印数字
1. 题目理解
核心需求:将由0、1、2、3组成的数字n
,转换为5×5网格的自定义表示形式。每个数字对应一个5×5的图案,输出时需将数字的每个字符对应的图案按列拼接(即每行输出所有数字对应行的字符)。
2. 已知图案定义
根据题目描述,0、1、2、3的5×5图案如下(每行5个字符,共5行):
数字 | 第0行 | 第1行 | 第2行 | 第3行 | 第4行 |
---|---|---|---|---|---|
0 | ..... | .***. | ..... | ||
1 | ****. | ****. | ****. | ||
2 | ..... | ..... | .**** | ..... | |
3 | ****. |
3. 解题思路
- 存储图案:用二维数组
digitPatterns
存储0-3的5×5图案(行索引0-4,数字索引0-3); - 读取输入:将输入的数字
n
转为字符串(便于逐字符处理); - 拼接输出:对5行中的每一行,依次拼接每个数字对应行的字符,组成最终的5行输出。
4. 完整代码与详细注释
#include <iostream>
#include <string>
using namespace std;
int main() {
// 存储0-3的5×5图案:digitPatterns[行号][数字] = 对应行的字符串
// 行号0-4,数字0-3
string digitPatterns[5][4] = {
{".....", "****.", ".....", "....."}, // 第0行
{".***.", "****.", "****.", "****."}, // 第1行
{".***.", "****.", ".....", "....."}, // 第2行
{".***.", "****.", ".****", "****."}, // 第3行
{".....", "****.", ".....", "....."} // 第4行
};
string n; // 用字符串存储输入的数字(避免大数处理问题)
cin >> n;
// 输出5行结果:每行由所有数字的对应行拼接而成
for (int row = 0; row < 5; row++) { // 遍历5行
string line = ""; // 存储当前行的拼接结果
for (char c : n) { // 遍历输入数字的每个字符
int digit = c - '0'; // 字符转数字(如'1'→1)
line += digitPatterns[row][digit]; // 拼接当前数字的对应行
}
cout << line << endl; // 输出当前行
}
return 0;
}
5. 输入输出示例
输入样例:12230
输出逻辑:
- 数字依次为'1','2','2','3','0';
- 第0行:1的第0行(****.)+2的第0行(.....)+2的第0行(.....)+3的第0行(.....)+0的第0行(.....)→
****.....................
; - 第1行:1的第1行(.)+2的第1行(.)+2的第1行(.)+3的第1行(.)+0的第1行(.***.)→
****.****.****.****..***.
; - 以此类推,最终输出与题目样例一致。
6. 关键知识点解析
- 二维数组:
digitPatterns
通过行和数字索引快速定位图案,体现了多维数组在结构化数据存储中的应用; - 字符串处理:将数字转为字符串便于逐字符遍历,拼接字符串实现图案的横向组合;
- 循环嵌套:外层循环控制输出行(0-4),内层循环控制每个数字的图案拼接,体现了嵌套循环在多维数据处理中的作用。
三、总结
本文通过两个GESP编程题,讲解了C++中结构体、数组、字符串、排序、查找等核心知识点:
- 图像压缩重点在于数据统计、排序和进制转换,适合理解复杂数据的处理流程;
- 打印数字重点在于二维数组存储和字符串拼接,适合掌握结构化数据的应用。
两道题均体现了“问题拆解→数据建模→分步实现”的编程思维,初学者可通过模仿代码、修改参数(如调整排序规则、更换图案)加深理解。
0 条评论
目前还没有评论...