试题编号:
2023-06-11-04-C-02
试题名称:图像压缩
时间限制:
1.0s
内存限制:
128.0MB
【问题描述】
图像是由很多的像素点组成的。如果用
0
表示黑,
255
表示
白,
0
和
255
之间的值代表不同程度的灰色,则可以用一个字节表达
一个像素(取值范围为十进制
0-255
、十六进制
00-FF
)。这样的像素
组成的图像,称为
256
级灰阶的灰度图像。
现在希望将
256
级灰阶的灰度图像压缩为
16
级灰阶,即每个
像素的取值范围为十进制
0-15
、十六进制
0-F
。压缩规则为:统计出
每种灰阶的数量,取数量最多的前
16
种灰阶(如某种灰阶的数量与另
外一种灰阶的数量相同,则以灰阶值从小到大为序),分别编号
0-F
(最多的编号为
0
,以此类推)。其他灰阶转换到最近的
16
种灰阶之
一,将某个点灰阶数与
16
种灰阶种的一种相减,绝对值最小即为最
近,如果绝对值相等,则编号较小的灰阶更近。
【输入描述】
输入第
1
行为一个正整数
N
,表示接下来有
N
行数据组成一
副
256
级灰阶的灰度图像。约定
10≤N≤20
。
第
2
行开始的
N
行,每行为长度相等且为偶数的字符串,每
两个字符用十六进制表示一个像素。约定输入的灰度图像至少有
16
种
灰阶。约定每行最多
20
个像素。
【输出描述】
第一行输出压缩选定的
16
种灰阶的十六进制编码,共计
32
个字符。
第二行开始的
N
行,输出压缩后的图像,每个像素一位十六
进制数表示压缩后的灰阶值。
【样例输入
1
】
10
00FFCFAB00FFAC09071B5CCFAB76
00AFCBAB11FFAB09981D34CFAF56
01BFCEAB00FFAC0907F25FCFBA65
10FBCBAB11FFAB09981DF4CFCA67
00FFCBFB00FFAC0907A25CCFFC76
00FFCBAB1CFFCB09FC1AC4CFCF67
01FCCBAB00FFAC0F071A54CFBA65
10EFCBAB11FFAB09981B34CFCF67
01FFCBAB00FFAC0F071054CFAC76
1000CBAB11FFAB0A981B84CFCF66
【样例输出
1
】
ABCFFF00CB09AC07101198011B6776FC
321032657CD10E
36409205ACC16D
B41032657FD16D
8F409205ACF14D
324F326570D1FE
3240C245FC411D
BF4032687CD16D
8F409205ACC11D
B240326878D16E
83409205ACE11D
【样例解释
1
】 灰阶
AB
、
CF
和
FF
出现
14
次,
00
出现
9
次,
CB
出现
9
次,
09
出现
7
次,
AC
出现
6
次,
07
出现
5
次,
10
、
11
和
98
出现
4
次,
01
、
1B
、
67
、
76
和
FC
出现
3
次。
【解析】直接模拟,详见代码
#include <bits/stdc++.h>
#include <cstring>
using namespace std;
int toint(char c){//十六进制转10进制
if (c>='A'&&c<='F'){
return c-'A'+10;
}
return c-'0';
}
int a[25][25];//保存输入数据
struct node{//不同灰度的数量
int hd;//灰度
int sl;//数量
}c[300];
void myprint(int x){//将10进制打印为16进制
char c;
if (x>9){
c='A'+x-10;
}else{
c=x+'0';
}
cout<<c;
}
bool cmp(node x,node y){//按出现的数量从大到小排序,数量相同,灰度小的排前边
if (x.sl==y.sl) return x.hd<y.hd;
return x.sl>y.sl;
}
int main() {
int n;
cin>>n;
string s;
for (int i=1;i<=n;i++){
cin>>s;
int t;
for (int j = 0; j < s.size(); j += 2) {//转换为10进制保存到数组中
t = toint(s[j]) * 16 + toint(s[j + 1]);
a[i][j / 2 + 1] = t;
c[t].sl++;
}
}
for (int i=1;i<=255;i++){//初始化灰度值
c[i].hd=i;
}
sort(c,c+256,cmp);//排序
for (int i=0;i<16;i++){//打印排名前16
myprint(c[i].hd/16);
myprint(c[i].hd%16);
}
cout<<endl;
int len=s.length()/2;
for (int i=1;i<=n;i++){//找到每个像素点的近似值
for (int j=1;j<=len;j++){
int cha=256;
int p=0;
for (int k=0;k<=15;k++){
if (abs(c[k].hd-a[i][j])<cha){
cha=abs(c[k].hd-a[i][j]);
p=k;
}
}
myprint(p);//打印
}
cout<<endl;
}
return 0;
}
感谢之江学院石老师提供的更简练的代码:
#include<bits/stdc++.h>
using namespace std;
string s[23], p = "0123456789ABCDEF";
int n, his[256], cn, a[23][23], t[16];
vector<array<int, 2>>v;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> s[i];
cn = s[i].size() / 2;
for(int j = 0; j < cn; j++)
his[a[i][j] = p.find(s[i][2*j])*16 + p.find(s[i][2*j+1])]++;
}
for(int i = 0; i < 256; i++)
v.push_back({-his[i], i});
sort(v.begin(), v.end());
for(int i = 0; i < 16; i++)
printf("%02X", v[i][1]);
for(int i = 0; puts(""), i < n; i++)
for(int j = 0; j < cn; j++)
{
for(int k = 0; k < 16; k++)
t[k] = abs(v[k][1]-a[i][j]);
printf("%X", min_element(t, t + 16) - t);
}
}