【題目概述】
CCF的第三題一般爲數據/字符串處理題,比第一題和第二題要難很多。該題不需要用到很難的數據結構,但是處理的數據比較繁瑣,並且對輸出格式要求十分的嚴格,稍微不一樣就不得分,所以一定要細心的讀完題目,認真瞭解該題要我們做什麼後再開始編程。另外第三題給的測試數據集一般是不夠用的,我們要多設計一些測試數據集,專挑特殊情況的測試集測試才能減少錯誤的發生。
第三題字符畫大致題意爲將一張大圖分成若干個小塊,計算每一小塊的平均像素,並將處理後的平均像素按一定順序輸出。這道題相當於是對圖片進行壓縮的操作。
關於該題的描述廢話有點多,比如該題根本不會用到前景色,但是花費了很長篇幅來敘述前景色,對考生進行了誤導,加大了題目的難度。
【輸入輸出】
輸入格式爲第一行輸入大圖的寬m和高n,第二行輸入小塊的寬p和高q,接下來的m*n行依次輸入大圖的像素,按從左到右,從上到下的順序輸入。
輸出格式爲依次輸出小塊的平均像素,按從左到右,從上到下的順序輸出爲一行,每次到行尾時輸出/x0A表示換行。並按照題目要求在相應位置加入轉義序列ESC[48;2;...m(\x1B\x5B\x34\x38\x3B\x32\x3B...\x6D)或ESC[0m(即\x1B\x5B\x30\x6D)。
輸出格式要注意以下幾點:
- 初始時終端的背景色都爲默認值(背景黑色);
- 如果下一個字符的顏色剛好與默認值完全相同,應當直接使用重置轉義序列ESC[0m(即\x1B\x5B\x30\x6D),而非手動更改顏色;
- 如果某個字符的背景色與其前一個字符相同則直接在前一個字符後輸入“空格”(\x20);
- 在每行(遇到/x0A幾位換行)結尾處,如果終端顏色不是默認值,應該重置終端的顏色狀態。
【思路】
- 根據題中所給的m和n的取值範圍,這裏可以創建一個三維數組unsigned char pixel[1080][1920][3]來存儲圖片像素R,G,B。存儲時需要將#xxxxxx轉換爲對應的R,G,B,並對特殊情況#x和#xxx做相應的處理。
- 計算每一小塊像素的平均值,並將計算出的像素值的ASCII編碼轉義爲16進制編碼形式,如像素爲255,55,5需要轉義爲/x32/x35/x35,/x35/x35,/x35。
- 定義PR,PG,PB保存上一次像素值,每進行第一行或者新一行的運算時需要將PR,PG,PB初始化爲默認值0。然後將計算的像素平均值與PR,PG,PB或者默認值0,0,0比較來輸出相應的格式
具體解釋請看代碼分析:
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include <sstream>
using namespace std;
unsigned char pixel[1080][1920][3];
//記錄圖片的寬和高
int m, n;
//記錄小塊圖片的寬和高
int p, q;
//將輸入的像素計算爲具體數值
unsigned char compute(char c1, char c2)
{
unsigned char ch = 0;
if (c1 >= 'a')
ch = (c1 - 'a' + 10) * 16;
else
ch = (c1 - '0') * 16;
if (c2 >= 'a')
ch += c2 - 'a' + 10;
else
ch += c2 - '0';
return ch;
}
//將小塊的平均像素轉換爲合適的格式
void transfer(int num1, string &num2)
{
stringstream on;
on << num1;
on >> num2;
on.clear();
if (num2.size() == 1)
{
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[0];
}
else if (num2.size() == 2)
{
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[0];
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[1];
}
else
{
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[0];
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[1];
on << "\\x";
on << hex << setw(2) << uppercase << (int)num2[2];
}
on >> num2;
}
int main()
{
cin >> m >> n >> p >> q;
//RGB分別用於記錄三原色的數值,N爲小塊圖片的像素數
int R, G, B, N = p * q;
//SR SG SB爲R G B對應的16進制字符串形式
string SR, SG, SB;
//PR PG PB分別用於記錄上一小塊圖片的三原色的數值,初始爲0,表默認值
int PR = 0, PG = 0, PB = 0;
//color用於輸入像素#aaaaaa
string color;
//輸入像素,並將輸入的像素處理好後存入到pixel數組
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> color;
//判斷特殊情況如#a,將其改爲#aaaaaa
if (color.size() == 2)
color += string(5, color[1]);
//判斷特殊情況如#aaa,將其改爲#aaaaaa
else if (color.size() == 4)
color = string(1, color[0]) + string(2, color[1]) + string(2, color[2]) + string(2, color[3]);
//cout << color << endl;
pixel[i][j][0] = compute(color[1], color[2]);
pixel[i][j][1] = compute(color[3], color[4]);
pixel[i][j][2] = compute(color[5], color[6]);
}
}
//進行循環輸出小塊圖片的平均像素,n/q行
for (int i = 0; i < n / q; i++)
{
//每進行新一行的計算時都需要把PR PG PB重置
PR = 0, PG = 0, PB = 0;
//共有m/p列
for (int j = 0; j < m / p; j++)
{
//每進行一小塊像素計算時,都需要把像素R,G,B重置
R = 0, G = 0, B = 0;
//計算每一小塊的像素
for (int k = i * q; k < i * q + q; k++)
{
for (int h = j * p; h < j * p + p; h++)
{
R += pixel[k][h][0];
G += pixel[k][h][1];
B += pixel[k][h][2];
}
}
//對每一小塊的像素去平均準,並轉換爲字符串形式
R /= N; G /= N; B /= N;
transfer(R, SR); transfer(G, SG); transfer(B, SB);
//將計算的像素平均值與PR,PG,PB或者默認值0,0,0比較來輸出對應數據格式
if (!(R == PR && G == PG && B == PB))
{
if (R == 0 && G == 0 && B == 0)
cout << "\\x1B\\x5B" << "\\x30\\x6D";
else
cout << "\\x1B\\x5B\\x34\\x38\\x3B\\x32\\x3B" << SR << "\\x3B" << SG << "\\x3B" << SB << "\\x6D";
}
cout << "\\x20";
PR = R, PG = G, PB = B;
//在每行結尾處,如果終端顏色不是默認值,應該重置終端的顏色狀態。
if (j == m / p - 1 && !(R == 0 && G == 0 && B == 0))
cout << "\\x1B\\x5B\\x30\\x6D";
}
//換行
cout << "\\x0A";
}
return 0;
}
測試數據集:
輸入1
1 1
1 1
#010203
輸出1
\x1B\x5B\x34\x38\x3B\x32\x3B\x31\x3B\x32\x3B\x33\x6D\x20\x1B\x5B\x30\x6D\x0A
輸入2
2 2
1 2
#111111
#0
#000000
#111
輸出2
\x1B\x5B\x34\x38\x3B\x32\x3B\x38\x3B\x38\x3B\x38\x6D\x20\x20\x1B\x5B\x30\x6D\x0A
輸入3
1 1
1 1
#0
輸出3
\x20\x0A
輸入4
2 2
2 1
#111111
#0
#000000
#111
輸出4
\x1B\x5B\x34\x38\x3B\x32\x3B\x38\x3B\x38\x3B\x38\x6D\x20\x1B\x5B\x30\x6D\x0A\x1B\x5B\x34\x38\x3B\x32\x3B\x38\x3B\x38\x3B\x38\x6D\x20\x1B\x5B\x30\x6D\x0A
輸入5
2 2
2 1
#111111
#0
#000000
#000
輸出5
\x1B\x5B\x34\x38\x3B\x32\x3B\x38\x3B\x38\x3B\x38\x6D\x20\x1B\x5B\x30\x6D\x0A\x20\x0A
輸入6
3 2
1 2
#0
#0
#010101
#010102
#0
#0
輸出6
\x1B\x5B\x34\x38\x3B\x32\x3B\x30\x3B\x30\x3B\x31\x6D\x20\x1B\x5B\x30\x6D\x20\x20\x0A
輸入7
1 2
1 2
#123456
#abcdef
輸出7
\x1B\x5B\x34\x38\x3B\x32\x3B\x39\x34\x3B\x31\x32\x38\x3B\x31\x36\x32\x6D\x20\x1B\x5B\x30\x6D\x0A
輸入8
2 1
2 1
#654321
#fedcba
輸出8
\x1B\x5B\x34\x38\x3B\x32\x3B\x31\x37\x37\x3B\x31\x34\x33\x3B\x31\x30\x39\x6D\x20\x1B\x5B\x30\x6D\x0A