gesp(C++四级)(4)洛谷:B3851:[GESP202306 四级] 图像压缩

题目描述
图像是由很多的像素点组成的。如果用  
     
      
       
       
         0 
        
       
      
        0 
       
      
    0 表示黑, 
     
      
       
       
         255 
        
       
      
        255 
       
      
    255 表示白, 
     
      
       
       
         0 
        
       
      
        0 
       
      
    0 和  
     
      
       
       
         255 
        
       
      
        255 
       
      
    255 之间的值代表不同程度的灰色,则可以用一个字节表达一个像素(取值范围为十进制 0-255、十六进制 00-FF)。这样的像素组成的图像,称为  
     
      
       
       
         256 
        
       
      
        256 
       
      
    256 级灰阶的灰度图像。
现在希望将  
     
      
       
       
         256 
        
       
      
        256 
       
      
    256 级灰阶的灰度图像压缩为  
     
      
       
       
         16 
        
       
      
        16 
       
      
    16 级灰阶,即每个像素的取值范围为十进制 0-15、十六进制 0-F。压缩规则为:统计出每种灰阶的数量,取数量最多的前  
     
      
       
       
         16 
        
       
      
        16 
       
      
    16 种灰阶(如某种灰阶的数量与另外一种灰阶的数量相同,则以灰阶值从小到大为序),分别编号 0-F(最多的编号为 0,以此类推)。其他灰阶转换到最近的  
     
      
       
       
         16 
        
       
      
        16 
       
      
    16 种灰阶之一,将某个点的灰阶值(灰度,而非次数)与  
     
      
       
       
         16 
        
       
      
        16 
       
      
    16 种灰阶中的一种相减,绝对值最小即为最近,如果绝对值相等,则编号较小的灰阶更近。
输入格式
输入第 1 1 1 行为一个正整数 n ( 10 ≤ n ≤ 20 ) n(10\le n \le 20) n(10≤n≤20),表示接下来有 n n n 行数据组成一副 256 256 256 级灰阶的灰度图像。
第 2 2 2 行开始的 n n n 行,每行为长度相等且为偶数的字符串,每两个字符用十六进制表示一个像素。约定输入的灰度图像至少有 16 16 16 种灰阶。约定每行最多 20 20 20 个像素。
输出格式
第一行输出压缩选定的 16 16 16 种灰阶的十六进制编码,共计 32 32 32 个字符。
第二行开始的 n n n 行,输出压缩后的图像,每个像素一位十六进制数表示压缩后的灰阶值。
样例 #1
样例输入 #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 1 1 解释】
灰阶 AB、CF 和 FF 出现  
     
      
       
       
         14 
        
       
      
        14 
       
      
    14 次,00 出现  
     
      
       
       
         10 
        
       
      
        10 
       
      
    10 次,CB 出现
  
     
      
       
       
         9 
        
       
      
        9 
       
      
    9 次,09 出现  
     
      
       
       
         7 
        
       
      
        7 
       
      
    7 次,AC 出现  
     
      
       
       
         6 
        
       
      
        6 
       
      
    6 次,07 出现  
     
      
       
       
         5 
        
       
      
        5 
       
      
    5 次,10、11
 和 98 出现  
     
      
       
       
         4 
        
       
      
        4 
       
      
    4 次,01、1B、67、76 和 FC 出现  
     
      
       
       
         3 
        
       
      
        3 
       
      
    3 次。
AC代码(100分)
#include<bits/stdc++.h>
using namespace std;
int n,a[30][30];//a数组用于保存转换后的输入数据 
string s;
//创建结构体:10进制的灰度及其出现次数 
struct node{
	int grey;//灰度 
	int cnt;//出现次数 
}p[260]; 
//十六进制字符转10进制整数
int to10(char c){
	int ans;
	if(c>='0' && c<='9') ans=c-'0';
	else ans=c-'A'+10;
	return ans;
} 
//排序规则函数
bool cmp(node x,node y){
	if(x.cnt!=y.cnt) return x.cnt>y.cnt;//优先按灰度出现的次数降序 
	else return x.grey<y.grey;//其次按灰度编号升序 
} 
//10进制整数打印为16进制 
void print16(int x){
	char c;
	if(x>9) c='A'+x-10;
	else c='0'+x;
	cout<<c; 
} 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){//n行 
		cin>>s;//输入每行的字符串
		//两个字符一组,将16进制转换位10进制保存到数组中
		int x;
		for(int j=0;j<s.size();j+=2){
			x=to10(s[j])*16+to10(s[j+1]);//调用字符转十进制函数 
			a[i][j/2+1]=x;//存到a数组中
			p[x].cnt++; //统计灰度出现的次数 
		} 
	}
	//初始化灰度
	for(int i=0;i<256;i++){
		p[i].grey=i;
	} 
	//p数组排序
	sort(p,p+256,cmp); 
	//输出灰度排名前16:需将10进制数字转换回16进制字符 
	for(int i=0;i<16;i++){
		print16(p[i].grey/16);//打印两位十六进制中的第1位 
		print16(p[i].grey%16);//打印两位十六进制中的第2位
	} 
	cout<<endl;
	//输出压缩后的图像
	int len=s.size()/2;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=len;j++){
			int minc=256;//存:最小差绝对值
			int b=0;//存:最接近的灰度编号 
			for(int k=0;k<=15;k++){
				if(abs(p[k].grey-a[i][j])<minc){
					minc=abs(p[k].grey-a[i][j]);
					b=k;//更新编号 
				}
			}
			//打印找到的与其最近的灰度编号的16进制
			print16(b); 
		}
		cout<<endl;
	} 
	return 0;
}
文末彩蛋:
点击王老师青少年编程主页有更多精彩内容



















