CCF上面的题目有一些跟我平时做的算法题很不一样,就比如说这道。这道题可以说是将输入输出的方法考察的淋漓尽致,中间的过程反而不是那么困难了。而且我发现ccf经常出这种背景很长,需要仔细理解的问题,一般第三题之后的题目都呈现这样的特点。

先放题目
我的代码如下,main函数的长度尽量写短了,像这种输入输出比较复杂的题我感觉还是用C++比较简单,因为Java的格式化输入输出我觉得不如C++/C顺手,对字符串的处理整一手sscanf,sprintf(如果在Visual Studio中不想用 sscanf_s就药要在开头加一个宏定义 _CRT_SECURE_NO_WARNINGS),对应的十六进制输入输出就很好用(例如%04x,就是指以小写的4位16进制输出,不足4位补零),代码如下
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<string>
#include<string.h>
using namespace std;
/*test:
2 2
1 2
#111111
#0
#000000
#111
*/
struct pixel{
int R, G, B;
pixel(int r, int g, int b) : R(r), G(g), B(b) {};
};
bool equalPixel(pixel A, pixel B) {
if (A.B == B.B && A.G == B.G && A.R == B.R) {
return true;
}
else
{
return false;
}
}
string toHex(int c) {
char hex[20];
if (c / 100 != 0) { //3位数
int h = c / 100;
int t = (c % 100) / 10;
int g = c - h * 100 - t * 10;
sprintf(hex, "\\x3%d\\x3%d\\x3%d", h, t, g);
}
else if (c / 10 != 0) { //2位数
int t = c / 10;
int g = c - 10 * t;
sprintf(hex, "\\x3%d\\x3%d", t, g);
}
else {
sprintf(hex, "\\x3%d", c);
}
return string(hex);
}
pixel getPixel(string s) {
int length = s.length();
char fullFormat[8];
//先将简写形式转化为完全形式,再提取关键字
if (length == 2) {
char c;
sscanf(s.c_str(), "#%c", &c);
sprintf(fullFormat, "#%c%c%c%c%c%c", c, c, c, c, c, c);
}
else if (length == 4) {
char a, b, c;
sscanf(s.c_str(), "#%c%c%c", &a, &b, &c);
sprintf(fullFormat, "#%c%c%c%c%c%c", a, a, b, b, c, c);
}
else if (length == 7) {
strcpy(fullFormat, s.c_str());
}
int r, g, b;
sscanf(fullFormat, "#%02x%02x%02x", &r, &g, &b);
return pixel(r, g, b);
}
pixel getAverage(vector<vector<pixel>>& pixelMatrix, int iStart, int jStart, int p, int q) {
int r, g, b;
r = 0; g = 0; b = 0;
int pxnum = p * q;
for (int i = iStart; i < iStart + q; i++) {
for (int j = jStart; j < jStart + p; j++) {
r += pixelMatrix[i][j].R;
g += pixelMatrix[i][j].G;
b += pixelMatrix[i][j].B;
}
}
return pixel(r / pxnum, g / pxnum, b / pxnum);
}
vector<vector<pixel>> getBlockMartix(vector<vector<pixel>> &pixelMatrix, int m, int n, int p, int q) {
//int blockLineNumber = n / q; //要输出这么多\n
//int blockRowNumber = m / p; //一排色块有多少个
vector<vector<pixel>> blockMatrix; //色块的数据同样用pixel存储
for (int i = 0; i < n; i += q) {
vector<pixel> blockLine;
for (int j = 0; j < m; j += p) {
pixel block = getAverage(pixelMatrix, i, j, p, q);
blockLine.push_back(block);
}
blockMatrix.push_back(blockLine);
}
return blockMatrix;
}
int main() {
int m, n; //图片宽高
cin >> m >> n;
int p, q; //色块宽高
cin >> p >> q;
cin.ignore(); //将缓冲区内多出来的回车符消耗掉
//int pixelNumber = m * n;
vector<vector<pixel>> pixelMatrix;
for (int i = 0; i < n; i++) { //逐行输入
vector<pixel> pixelLine;
for (int j = 0; j < m; j++) {
string colorStr;
getline(cin, colorStr);
pixel input = getPixel(colorStr);
pixelLine.push_back(input);
}
pixelMatrix.push_back(pixelLine);
}
//到这里像素矩阵就构造完成了
int blockLineNumber = n / q; //要输出这么多\n
int blockRowNumber = m / p; //一排色块有多少个
vector<vector<pixel>> blockMatrix; //色块的数据同样用pixel存储
blockMatrix = getBlockMartix(pixelMatrix, m, n, p, q);
//下面开始输出
pixel lastBlock(0, 0, 0);
pixel defaultBlock(0, 0, 0);
for (int i = 0; i < blockLineNumber; i++) {
for (int j = 0; j < blockRowNumber; j++) {
pixel thisBlock = blockMatrix[i][j];
if (equalPixel(thisBlock, lastBlock)) { //如果与上一个色块相同,直接输出空格即可
cout << "\\x20"; //输出空格
}
else if (equalPixel(thisBlock, defaultBlock)) { //如果与默认颜色相同,输出重置转义序列
cout << "\\x1B\\x5B\\x30\\x6D"; //重置转义序列
cout << "\\x20"; //输出空格
lastBlock = thisBlock;
}
else {
cout << "\\x1B\\x5B\\x34\\x38\\x3B\\x32\\x3B"; //输出"ESC[48;2;"
cout << toHex(thisBlock.R);
cout << "\\x3B"; //输出分号
cout << toHex(thisBlock.G);
cout << "\\x3B"; //输出分号
cout << toHex(thisBlock.B);
cout << "\\x6D\\x20"; //输出m和空格
lastBlock = thisBlock;
}
}
if (!equalPixel(lastBlock, defaultBlock)) { //如果输出完一行后颜色不是默认值,应该恢复终端颜色
cout << "\\x1B\\x5B\\x30\\x6D"; //重置转义序列
lastBlock = defaultBlock;
}
cout << "\\x0A"; //换行
}
return 0;
}