### Day13:1.201909-3
#### 1.201909-3:字符画(大模拟)
(1)自己写出来是0分,但是还是没有找到问题所在,但整体思路没有问题,步骤一处理像素颜色输入(转化格式) 和步骤二计算块像素平均值都调试实现了,步骤三输出格式转变可能出现了问题,直接学习满分代码思路和写法。
(2)代码:
```
#include <bits/stdc++.h>
using namespace std;
struct RGB{
int R;
int G;
int B;//不习惯写数组,还是分开写成三个
};
int char2digit(char c){//单个16进制字符
if(c>='0' && c<='9') return c-'0';
else if(c>='a' && c<='f') return c-'a'+10;
else if(c>='A' && c<='F') return c-'A'+10;
}
int string2digit(string s){//多个16进制字符组成的字符串
int sum=0;
for(int i=0;i<2;i++){
sum=sum*16+char2digit(s[i]);
}
return sum;
}
bool isEqual(const RGB& a,const RGB& b){
if(a.R==b.R && a.G==b.G && a.B==b.B) return true;
return false;
}
void output1(string s){
for(int i=0;i<s.size();i++){
cout<<"\\x"<<hex<<uppercase<<setw(2)<<int(s[i]);//如果进行十六进制输出
}
}
void output2(int num)
{
string s = to_string(num); // 保持行为与 printstr 一致
for (int i = 0; i < s.size(); i++)
{
cout << "\\x" << hex << uppercase << setw(2) << int(s[i]); // 输出字符的 ASCII 码的十六进制表示
}
}
int main(){
ios::sync_with_stdio(false);
int n,m,p,q;
cin>>m>>n>>p>>q;
vector<vector<RGB>> vImages(n,vector<RGB>(m));
//处理像素颜色输入(转化格式)
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
string str;
cin>>str;
str.erase(0,1);
if(str.size()==1){
str=string(6,str[0]);//string类的构造函数
}
else if(str.size()==3){
str=string(2,str[0])+string(2,str[1])+string(2,str[2]);
}
RGB t;
t.R=string2digit(str.substr(0,2));
t.G=string2digit(str.substr(2,2));
t.B=string2digit(str.substr(4,2));
vImages[i][j]=t;
}
}
//计算块像素平均值
int block_n=n/q;
int block_m=m/p;//单独算一下
vector<vector<RGB>> vBlocks(block_n,vector<RGB>(block_m));
for(int i=0;i<block_n;i++){
for(int j=0;j<block_m;j++){
int RR=0,GG=0,BB=0;
for(int k=i*q;k<(i+1)*q;k++){
for(int z=j*p;z<(j+1)*p;z++){
RR+=vImages[k][z].R;
GG+=vImages[k][z].G;
BB+=vImages[k][z].B;
}
}
vBlocks[i][j].R=RR/(p*q);
vBlocks[i][j].G=GG/(p*q);
vBlocks[i][j].B=BB/(p*q);
}
}
//输出格式转变(重点)
RGB last={0,0,0},init={0,0,0}; //重点构造,原来代码没有初始化这边导致不好判断,但记得加上结构体比较函数
for(int i=0;i<block_n;i++){
RGB cur={0,0,0};
for(int j=0;j<block_m;j++){
cur=vBlocks[i][j];//现在的
if(!isEqual(cur,last)){//先判断如果某个字符的前景色/背景色与其前一个字符相同,而不是先判断默认,因为上一个颜色也可能是默认
if(isEqual(cur,init)){//如果下一个字符的颜色刚好与默认值完全相同
cout<<"\\x1B\\x5B\\x30\\x6D";//ESC[0m
last=init;//这个赋值很关键
}
else{
//关注如何输出
//0分
// cout<<"\\x1B\\x5B\\34\\x38\\x3B\\x32\\x3B";
// output2(cur.R);
// cout<<"\\x3B";//;
// output2(cur.G);
// cout<<"\\x3B";//;
// output2(cur.B);
// cout<<"\\x6D";//m
//满分
cout<<"\\x1B";
output1("[48;2;" + to_string(cur.R) + ";" + to_string(cur.G) + ";" + to_string(cur.B) + "m");
last=cur;
}
}
cout<<"\\x20";//一个块处理完都要输出一个空格
}
if(!isEqual(last,init)){//每行输出完后判断如果终端颜色不是默认值,你应该重置终端的颜色状态
cout<<"\\x1B\\x5B\\x30\\x6D";//ESC[0m
last=init;//这个赋值很容易遗漏
}
cout<<"\\x0A";//换行,一般都是最后做
}
return 0;
}
```
(3)
**重点错误**:
1.**思路逻辑错误**:先判断如果某个字符的前景色/背景色与其前一个字符相同,而不是先判断默认,因为上一个颜色也可能是默认 ,这个判断思路一开始写错了,且判断是默认值了或者每一行最后不是默认,last要赋值init
2.**判断特殊条件方法错误**:一开始用bool值和逐个判断RGB是否为0来排除特殊条件,但是会遗漏,且无法把默认值赋给上一个,所以这种**跟前一个比较的题要构造last和cur,并写出对应的isEqual函数(结构体需要)**
3.**题目理解误差**:空格是每个块设置完都要输出,所以是最内层循环最后输出,换行是最后输出(比重置终端的颜色状态还后面,换行基本是最后一个),所以是每一行结束输出(内层循环遍历完成)
**重点学习输入输出**:
1.原来处理像素颜色输入(转化格式)是创建个string对象然后append方法改变再赋值,但是可以直接string类构造对象直接赋值
2.尽量写单个16进制字符转整数(**基础函数**),然后再写一个函数16进制字符串转整数(迭代),或者stoi(str.substr(0,2),nullptr,16);(stoi不仅可以转为10进制数,还能转为16进制数)
3.**16进制的输出**:
`cout<<"\\x"<<hex<<uppercase<<setw(2)<<int(s[i]);//如果进行十六进制输出 `
**注意**:`\x1B`之类的输出要写成`\\x1B`,16进制输出要加上hex,后面大小写和字宽
4.可以直接写成字符串拼接的形式,而不一个一个输出(**待解决的问题**):
`output1("[48;2;" + to_string(cur.R) + ";" + to_string(cur.G) + ";" + to_string(cur.B) + "m");`
(4)本题一开始被干扰了:- `ESC [ 38; 2; R; G; B m` 更改终端的**前景色(字符的颜色)** 为 (R,G,B)这个根本没有用到,且本题难点就是字符串的格式输入输出以及16进制的字符与16进制的数及10进制的数之间的转换