歡迎訪問我的CCF認證考試題解目錄哦
題目描述
算法設計
首先說明一下本題需要用到的10進制與16進制的轉換方法:
printf("%02X",n)
:將10進制數n輸出成16進制數,其中10~15用大寫字母A~F
表示,輸出的16進制數不足兩位在高位補0stoi(s,0,16)
:將16進制字符串s轉換成10進制數並返回
我們可以利用array<int,3>
來存儲一個RGB值,0~2
索引位置的元素分別存儲R、G、B顏色分量的值。我們利用一個二維數組image
來存儲整個圖像的像素點的RGB值。對於讀取到的每一行RGB字符串,如果該字符串只有2個字符(例如#a
),則在其末尾添加5個原字符串的末尾字符,擴充成6位;如果有4個字符(例如#abc
),則將其1~3
索引位置的字符各變成2位,轉換代碼爲rgb = "#" + string(2, rgb[1]) + string(2, rgb[2]) + string(2, rgb[3])
。對於7位的RGB字符串,利用字符串的substr
函數進行分割,並利用stoi
函數將其轉換成10進制數,存儲在一個array<int,3>
變量中。
然後我們定義RGB值start
和last
,分別存儲默認狀態下的背景色和上一個塊的背景色,均初始化爲{0,0,0}
。遍歷整個圖像image
,針對每一塊,得出RGB的平均值,如果當前塊的背景色和last
以及start
均不同,按16進制數輸出所有更改背景色代碼的字符;如果當前塊的背景色和start
相同,按16進制數輸出所有重置默認狀態代碼的字符;如果當前塊的背景色和last
相同,什麼也不做。然後在每一個塊後按16進制數輸出一個空格。在輸出每一行後,查看這一行最後一個塊和start
是否相同,如果不同需要重置成默認狀態,需要按16進制數輸出所有重置默認狀態代碼的字符,然後置last
爲默認狀態,按十六進制數輸出一個換行符即可。
注意點
本題要注意的地方極多,稍不留神,可能花很長時間寫的代碼提交之後只有0分……所以要格外留意。
- 本題只涉及到了更改背景色,並沒有涉及到前景色的更改。
- 對於每一個命令行中的字符,均需按16進制數輸出其ASCII碼,且輸出的16進制數要有2位,不足兩位在高位補0。
- RGB顏色分量值在0~255之間,不能用char存儲(char存儲範圍是
-128~127
),最好用int存儲。 - 對於命令行中的RGB值,要先將10進制的RGB值轉換成字符串,再按16進制數形式逐個字符地輸出。例如一個RGB的一個顏色分量值爲128,需要按16進制數的形式分別輸出
1
、2
、8
這3個字符,而不是直接按16進制數形式輸出128這個10進制數。 - 計算RGB顏色分量平均值要向0取整,所以直接使用C++中的除法運算就可以了。
- 輸出每一個塊之後都要按16進制數形式輸出一個空格,輸出每一行字符串後都要按16進制數形式輸出一個換行符。
- 輸出每一行字符之後,輸出換行符之前,要檢查當前RGB值是否是默認狀態,如果不是,要置爲默認狀態,且要更新代碼中的上一個狀態爲默認狀態。
- 如果某個狀態的RGB值與其前一個狀態相同,那麼什麼也不要做,直接輸出一個空格即可。
- 如果一個狀態的RGB值剛好與默認狀態完全相同,要使用重置轉義序列。
- 先檢查一個狀態是否與上一個狀態相同,再檢查是否與默認狀態相同。也就是說,假如上一個狀態就是默認狀態,當前狀態也是默認狀態,那麼無需輸出重置轉義序列,直接輸出一個空格即可。
C++代碼
#include <bits/stdc++.h>
using namespace std;
void output(string& s, array<int, 3> rgb = {0, 0, 0}) { //輸出
for (char c : s)
if (c == 'R' || c == 'G' || c == 'B') { //是RGB數值
string t = to_string(rgb[c == 'R' ? 0 : c == 'G' ? 1 : 2]); //將數值轉換成字符串
for (char cc : t) //遍歷字符串
printf("\\x%02X", cc); //輸出16進制數
} else
printf("\\x%02X", c); //輸出字符的16進制數
}
int main() {
string back = "\x1b[48;2;R;G;Bm", reset = "\x1b[0m"; //背景色和重置默認值字符串
int m, n, p, q;
cin >> m >> n >> p >> q;
vector<vector<array<int, 3>>> image(n); //圖像像素
string rgb;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> rgb;
if (rgb.size() == 2) //只有2位字符
rgb += string(5, rgb.back()); //字符串末尾添加5個末尾字符
else if (rgb.size() == 4) //只有4位字符
rgb = "#" + string(2, rgb[1]) + string(2, rgb[2]) + string(2, rgb[3]); //添加成爲6位
image[i].push_back({0, 0, 0});
for (int t = 0; t < 3; ++t) //計算RGB數值,將16進制數轉換成10進制
image[i].back()[t] = stoi(rgb.substr(2 * t + 1, 2), 0, 16);
}
}
array<int, 3> last = {0, 0, 0}, start = {0, 0, 0};
for (int i = 0; i < n / q; ++i) { //遍歷所有像素
for (int j = 0; j < m / p; ++j) {
array<int, 3> cur = {0, 0, 0};
for (int r = 0; r < q; ++r) { //計算塊內RGB顏色分量之和
for (int s = 0; s < p; ++s) {
for (int t = 0; t < 3; ++t)
cur[t] += image[i * q + r][j * p + s][t];
}
}
for (int t = 0; t < 3; ++t) //計算RGB顏色分量平均值
cur[t] /= p * q;
if (cur != last) //和上一個塊顏色分量不同
if (cur == start) //和默認顏色分量一致,輸出重置轉義序列
output(reset);
else
output(back, cur);
last = cur; //更新上一個狀態爲當前狀態
printf("\\x%02X", ' '); //每一個塊後輸出一個空格
}
if (last != start) //每一行結束後恢復默認狀態
output(reset);
last = start; //每一行結束後更新上一個狀態位默認狀態
printf("\\x%02X", '\n'); //每一行字符後輸出一個換行
}
return 0;
}