题目分析:
规定:每一个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
*/