CSP201909-3字符画

本文详细解析了如何处理字符画的RGB颜色平均计算,通过分割图片并计算每块图片的平均RGB值,实现字符画的颜色转换。文章介绍了结构体的使用、动态数组的创建及颜色转换的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题目分析:
规定:每一个RGB称之为一个像素点,分割之后的每一块称为一块图片,例如m*n = 6*8,p*q = 1*2,那么共有48个像素点,24块图片。
这道题挺坑的,明明只需要修改背景色进行,却把前景色的修改方式也告诉了你,让人误以为是通过修改前景色来改变字符的显示。其实如果理解了出题人的描述,这道题还是挺简单的。

定义一个结构体用来存储每个像素点的RGB,因为每个颜色分量不会超过255,因此将颜色分量定义成unsigned char类型的即可。定义构造函数,用初始化列表进行赋值,别忘了同时还要定义一个不带任何参数的构造函数,这属于C++的内容。

struct Pixel {	
	unsigned char r;
	unsigned char g;
	unsigned char b;
	Pixel(unsigned char r, unsigned char g, unsigned char b) :
		r(r), g(g), b(b) {};
	Pixel() {};
};

根据题目中的子任务说明,m小于等于1920,n小于等于1080,因此定义一个结构体数组,大小为1080*1920,此处要注意,m是字符画的宽度,在二维数组中相当于有m列,刚开始我定义的数组大小是1920*1080,评测之后只有80分,找了好长时间才发现是这儿的问题。
Pixel pixel[1080][1920];

再定义一个结构体,用来存储分割之后的每一块的图片的RGB,这个结构体还起着另一个作用——把每一块图片的所有的RGB分别相加,最后再除以p*q就是RGB的平均值,此处需要注意结构体成员的类型,考虑一下极端情况,字符画的大小为1920*1080,p*q为1*1,每个颜色分量都是255,那么加起来的和位1920*1080*255=528768000,小于int类型变量所能表示的最大数2147483647,因此结构体的成员可以是int型的。
struct Block {
	int r;
	int g;
	int b;
	Block() :r(0), g(0), b(0) {};
};

在定义Block类型的数组时,我采用的是动态内存分配,因为究竟有多少块图片是无法预先估计的,它最大可以达到1080*1920,即每一块图片就是一个像素点,但如果直接把数组定义成1080*1920,未免有些浪费存储空间,当然也不会超出本题的内存限制,我在这儿是用new定义的二维动态数组。
	Block ** block = new Block *[R];
	for (int i = 0; i < R; i++)
		block[i] = new Block[C];

现在剩下的问题是如何将每块图片的所有像素点进行相加,我举个例子来说明这个问题,假设现在图片的大小为m*n = 6*4,p*q = 1*2。则此幅图片的分割情况如下图所示。为了便于观察,把每一个像素点都加上了颜色,用黄色的方框把每一块图片圈了起来。为了描述方便,定义下面两个结构体数组
Pixel pixel[4][6];
Block block[2][6];

在这里插入图片描述

可以发现一个规律,对于pixel[i][j],那么block[i / q][j / p]恰好就是pixel[i][j]这个像素点所对应的图片块。如果不理解,可以自己对照着上图举几个例子看一下。比如pixel[2][3],它对应的像素块是block[1][3],用上面的公式,i / q = 2 / 2 = 1,j / p = 3 / 1 = 3。

			block[i / q][j / p].r += pixel[i][j].r;
			block[i / q][j / p].g += pixel[i][j].g;
			block[i / q][j / p].b += pixel[i][j].b;

在进行输出的时候,有一点需要注意,题目中说“如果某个字符的前景色/背景色与其前一个字符相同,或者对颜色的更改并不影响最终的显示效果,则不应该出现更改这个属性的控制序列”。这句话"或者"后面的内容是重点,刚开始我没看明白,导致测评一直是0分。这句话适用于输出的第一块图片的颜色就和默认背景色相同,因为它是第一块,所以不存在与其前一个字符相同的情况。这时候就不需要出现更改颜色的序列。其实只需要让前一个字符的初始颜色为(0,0,0)即可解决此问题。

在这里插入图片描述

源代码:

#include <bits/stdc++.h>
using namespace std;
//存放每一个像素点的值
struct Pixel {	
	unsigned char r;
	unsigned char g;
	unsigned char b;
	Pixel(unsigned char r, unsigned char g, unsigned char b) :
		r(r), g(g), b(b) {};
	Pixel() {};
};
Pixel pixel[1080][1920];	//巨坑
//存放每一块图片的像素值之和,并在最后求平均值,保存在此结构体里
struct Block {
	int r;
	int g;
	int b;
	Block() :r(0), g(0), b(0) {};
};

void trans(string &s) {	//将r,g,b转换为十六进制
	string result = "";
	for (int i = 0; i < s.size(); i++) 
		result += "\\x3" + s.substr(i, 1);	
	s = result;
}
int main()
{
	int m, n, p, q;	//m是宽度,n是高度
	int R, C;
	cin >> m >> n;
	cin >> p >> q;
	getchar();	//吸收多出来的换行
	//分割之后的图片总共有R行C列
	R = n / q;	
	C = m / p;

	//动态数组,block存放每个小块的颜色信息
	Block ** block = new Block *[R];
	for (int i = 0; i < R; i++)
		block[i] = new Block[C];

	string str;
	string r, g, b;
	for (int i = 0; i < n; i++)
		for(int j = 0;j < m;j++){
		getline(cin, str);
		//对各种输入形式进行处理
		if (str.length() == 7) {	//形如#111111的形式
			r = str.substr(1, 2);
			g = str.substr(3, 2);
			b = str.substr(5, 2);
		}
		else if (str.length() == 4) {	//形如#111的形式
			r = string(2,str[1]);
			g = string(2,str[2]);
			b = string(2,str[3]);
		}
		else {	//形如#0的形式
			r = string(2,str[1]);
			g = r;
			b = r;
		}
		//将每个像素分量转换成10进制数后存入pexel,请自行百度stoi函数的使用
		pixel[i][j] = { (unsigned char)stoi(r, nullptr, 16), (unsigned char)stoi(g, nullptr, 16), (unsigned char)stoi(b, nullptr, 16) };
	}
	//求每个小块所有像素点的r,g,b之和
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			block[i / q][j / p].r += pixel[i][j].r;
			block[i / q][j / p].g += pixel[i][j].g;
			block[i / q][j / p].b += pixel[i][j].b;
		}
	}
	//求每一个小块的颜色平均值
	for(int i = 0;i < R;i++)
		for (int j = 0; j < C;j++) {
			block[i][j].r /= p*q;
			block[i][j].g /= p*q;
			block[i][j].b /= p*q;
		}
	string result = "";	//最终的输出结果
	int before_r =0, before_g= 0, before_b = 0;	//记录上一次设置的颜色
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			if (block[i][j].r == before_r && block[i][j].g == before_g && block[i][j].b == before_b) {	//如果和上一个字符的颜色相同,或者改变颜色不影响显示效果
				result += "\\x20";
			}
			else if (block[i][j].r == 0 && block[i][j].g == 0 && block[i][j].b == 0) {	//如果和终端默认颜色相同
				result += "\\x1B\\x5B\\x30\\x6D\\x20";
			}
			else {
				string r = to_string(block[i][j].r);
				string g = to_string(block[i][j].g);
				string b = to_string(block[i][j].b);
				trans(r);
				trans(g);
				trans(b);
				result += "\\x1B\\x5B\\x34\\x38\\x3B\\x32\\x3B" + r + "\\x3B" + g + "\\x3B" + b + "\\x6D\\x20";
			}
			//记录当前的颜色,以便下次比较时使用
			before_r = block[i][j].r;
			before_g = block[i][j].g;
			before_b = block[i][j].b;
		}
		if (before_r != 0|| before_g != 0 || before_b != 0) {	//如果终端颜色不是默认值,则重置终端颜色
			result += "\\x1B\\x5B\\x30\\x6D";
		}
			result += "\\x0A";	//每一行末尾添加换行
			before_r = 0, before_g = 0, before_b = 0;
	}
	cout << result;
	return 0;
}

/*
2 2
1 2
#111111
#0
#000000
#111
*/

/*
4 4
1 2
#111
#222
#111
#222
#333
#444
#333
#444
#111
#222
#111
#222
#333
#444
#333
#444
*/
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值