CCF認證201909-3字符畫

歡迎訪問我的CCF認證考試題解目錄

題目描述

CCF認證201909-3字符畫題目描述

算法設計

首先說明一下本題需要用到的10進制與16進制的轉換方法:

  1. printf("%02X",n):將10進制數n輸出成16進制數,其中10~15用大寫字母A~F表示,輸出的16進制數不足兩位在高位補0
  2. stoi(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值startlast,分別存儲默認狀態下的背景色和上一個塊的背景色,均初始化爲{0,0,0}。遍歷整個圖像image,針對每一塊,得出RGB的平均值,如果當前塊的背景色和last以及start均不同,按16進制數輸出所有更改背景色代碼的字符;如果當前塊的背景色和start相同,按16進制數輸出所有重置默認狀態代碼的字符;如果當前塊的背景色和last相同,什麼也不做。然後在每一個塊後按16進制數輸出一個空格。在輸出每一行後,查看這一行最後一個塊和start是否相同,如果不同需要重置成默認狀態,需要按16進制數輸出所有重置默認狀態代碼的字符,然後置last爲默認狀態,按十六進制數輸出一個換行符即可。

注意點

本題要注意的地方極多,稍不留神,可能花很長時間寫的代碼提交之後只有0分……所以要格外留意。

  1. 本題只涉及到了更改背景色,並沒有涉及到前景色的更改。
  2. 對於每一個命令行中的字符,均需按16進制數輸出其ASCII碼,且輸出的16進制數要有2位,不足兩位在高位補0。
  3. RGB顏色分量值在0~255之間,不能用char存儲(char存儲範圍是-128~127),最好用int存儲。
  4. 對於命令行中的RGB值,要先將10進制的RGB值轉換成字符串,再按16進制數形式逐個字符地輸出。例如一個RGB的一個顏色分量值爲128,需要按16進制數的形式分別輸出128這3個字符,而不是直接按16進制數形式輸出128這個10進制數。
  5. 計算RGB顏色分量平均值要向0取整,所以直接使用C++中的除法運算就可以了。
  6. 輸出每一個塊之後都要按16進制數形式輸出一個空格,輸出每一行字符串後都要按16進制數形式輸出一個換行符。
  7. 輸出每一行字符之後,輸出換行符之前,要檢查當前RGB值是否是默認狀態,如果不是,要置爲默認狀態,且要更新代碼中的上一個狀態爲默認狀態
  8. 如果某個狀態的RGB值與其前一個狀態相同,那麼什麼也不要做,直接輸出一個空格即可。
  9. 如果一個狀態的RGB值剛好與默認狀態完全相同,要使用重置轉義序列。
  10. 先檢查一個狀態是否與上一個狀態相同,再檢查是否與默認狀態相同。也就是說,假如上一個狀態就是默認狀態,當前狀態也是默認狀態,那麼無需輸出重置轉義序列,直接輸出一個空格即可。

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章