不追求最精簡的代碼,追求最易讀懂的代碼,努力讓讀者understand,自己再寫出代碼
一、審題
按照題目的意思,每行輸入的區域顏色,沒有區分前景色,背景色。題目更改的都是背景色,因此默認都只更改背景色。
1、初始時終端的前景色和背景色都爲默認值(前景白色,背景黑色)
如果第一塊區域計算的前景色或背景色爲默認值,則不需要輸出到終端,但是要輸出空格。
2、如果下一個字符和顏色剛好與默認值完全相同,你應當直接使用重置轉義序列,而非手動更改顏色。
3、 如果某個字符的前景色/背景色與其前一個字符相同,或者對顏色的更改並不影響最終的顯示效果,則不應該出現更改這個屬性的控制序列。
能省就省,不要忘記輸出空格。
4、在輸每一行字符後,如果終端顏色不是默認值,你應該重置終端的顏色狀態。
每一行最後也需要輸出換行符 \n
5、需要處理特殊情況#aabbcc可以寫成#abc,#aaaaaa可以寫成#a
就三種情況,單獨拿出來處理一下okk
二、算法策略
1、顏色存儲,顏色換算,使用 dot [m][n] 標記任意座標的顏色。
struct dot{
int R;
int G;
int B;
dot(int a,int b,int c){
R = a;
G = b;
B = c;
}
}
將string轉換爲 dot,單獨拿出來一個函數。
3、區域計算,區域數量爲(m×n)/(p×q),行數爲n/q,使用vector 鏈接,注意區分行就行。
4、初始背景色,前景色,在每行開始的時候設置爲前驅,標記前驅來省略對顏色的更改。
5、模擬的時候不要漏掉題目給的情況,宏觀上嚴密把控後,再局部實現。
void outputRGB()
{
int size = list.size(); //區域的塊數
int gap = m/p; // 每行gap個區域
int i;
dot pre; //前一個區域
dot curr; //當前區域
for(i=0;i<size;i++)
{
curr = list[i];
if(i%gap == 0) // 1、每行第一個設置前驅爲默認背景色
pre = defaultDot;
if(isEqual(curr,pre)) // 2、當前顏色和前驅顏色相同,不做更改直接輸出一個空格
outputSpace();
else if(isEqual(curr,defaultDot)){ // 3、當前顏色爲默認背景顏色
outputDefaultRGB(); // 輸出重置終端,而不是當前顏色
outputSpace(); //輸出一個空格
}
else{ // 普通輸出
outputSingleRGB(curr);
}
if( (i+1)%gap ==0 && !isEqual(curr,defaultDot)){ // 每行最後一個不是默認顏色
outputDefaultRGB();
}
if( (i+1)%gap == 0 ) // 每行最後一個
printf(R"(\x0A)"); //輸出'\n'
pre = curr; // 更新前驅
}
}
確保上訴不漏掉題目所給出的情況後,再一一實現子函數。
最終AC代碼如下。
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
int m,n;
int p,q;
struct dot{
int R;
int G;
int B;
dot(int a,int b,int c){
R = a;
G = b;
B = c;
}
dot(){
R=G=B=0;
}
};
dot arr[1100][2000];
dot defaultDot(0,0,0); // 默認背景色
vector<dot> list;
int toAscii(char ch)
{
if(ch>='a')
return ch-'a'+10;
else
return ch-'0';
}
dot strToDot(string str)
{
str.erase(str.begin()); //刪除前導#
dot res;
int len = str.length();
if(len == 6){
res.R = toAscii(str[0])*16 + toAscii(str[1]);
res.G = toAscii(str[2])*16 + toAscii(str[3]);
res.B = toAscii(str[4])*16 + toAscii(str[5]);
}
else if(len == 1){
str += str[0];
res.R = res.G = res.B = toAscii(str[0])*16 + toAscii(str[1]);
}
else{ //len = 3
res.R = toAscii(str[0])*16 + toAscii(str[0]);
res.G = toAscii(str[1])*16 + toAscii(str[1]);
res.B = toAscii(str[2])*16 + toAscii(str[2]);
}
return res;
}
dot countDot(int row,int endRow,int col,int endCol)
{
dot res;
int i,j;
for(i=row;i<endRow;i++)
for(j=col;j<endCol;j++)
{
res.R += arr[i][j].R;
res.G += arr[i][j].G;
res.B += arr[i][j].B;
}
int area = (endRow-row)*(endCol-col);
res.R /= area;
res.G /= area;
res.B /= area;
return res;
}
bool isEqual(dot a,dot b)
{
return (a.R==b.R && a.G==b.G && a.B==b.B);
}
void outputDefaultRGB()
{
printf(R"(\x1B\x5B\x30\x6D)"); // ESC[0m
}
void outputNumber(int num)
{
string str;
str = to_string(num);
for(char i : str)
{
int a = int(i); //轉化爲ascii碼
printf(R"(\x)");
printf("%X",a);
}
}
void outputSingleRGB(dot curr)
{
printf(R"(\x1B\x5B\x34\x38\x3B\x32\x3B)"); // ESC[48;2;
outputNumber(curr.R);
printf(R"(\x3B)");
outputNumber(curr.G);
printf(R"(\x3B)");
outputNumber(curr.B);
printf(R"(\x6D\x20)"); // m+space
}
void outputSpace()
{
printf(R"(\x20)");
}
void outputRGB()
{
int size = list.size();
int gap = m/p; // 每行gap個區域
int i;
dot pre; //前一個區域
dot curr; //當前區域
for(i=0;i<size;i++)
{
curr = list[i];
if(i%gap == 0) // 1、每行第一個設置前驅爲默認背景色
pre = defaultDot;
if(isEqual(curr,pre)) // 2、當前顏色和前驅顏色相同,不做更改直接輸出一個空格
outputSpace();
else if(isEqual(curr,defaultDot)){ // 3、當前顏色爲默認背景顏色
outputDefaultRGB(); // 輸出重置終端,而不是當前顏色
outputSpace(); //輸出一個空格
}
else{ // 普通輸出
outputSingleRGB(curr);
}
if( (i+1)%gap ==0 && !isEqual(curr,defaultDot)){ // 每行最後一個不是默認顏色
outputDefaultRGB();
}
if( (i+1)%gap == 0 ) // 每行最後一個
printf(R"(\x0A)"); //輸出'\n'
pre = curr; // 更新前驅
}
}
int main() {
cin>>m>>n;
cin>>p>>q;
int i,j;
string str;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
cin>>str;
arr[i][j] = strToDot(str);
}
i = j = 1;
while( i<=n )
{
dot t = countDot(i,i+q,j,j+p);
list.push_back(t);
j += p;
if( j > m )
{
j = 1;
i += q;
}
}
outputRGB();
return 0;
}