CCFCSP认证 2019-03C字符画

文章描述了一道编程竞赛中的复杂问题,涉及到矩阵分块计算平均颜色,然后将颜色信息转化为ASCII编码和十六进制表示。题目设计巧妙,要求忽视前景色只考虑背景色,并注意矩阵下标的转换。作者分享了使用二维结构体数组优化存储和处理颜色信息的经验。
摘要由CSDN通过智能技术生成

题目大意

考点

各种进制转换与ascii编码 + 阅读理解

思路

这题个人认为是CSP历年第三题里最坑的一道,因为出题人比较拐弯抹角,没有直接告诉你一些条件。比如:这道题给出了前景色和背景色的概念,却没告诉你应该用哪种色,而是隔了好多行,藏了一句话:“空格用来绘制色块(呈现出的即为背景色)”。也就是说,本题只用到背景色!当时我注意到了,可是我不敢直接忽略前景色,犹豫了好久。

由于本题流程较复杂,所以读完题后脑海里要形成一个流程:矩阵分块求平均–>按顺序生成指令–>指令ascii码转十六进制,每个流程都要有一个大致思路。然后模拟即可。

另外值得注意的是,本题有三种颜色,最好是用二维结构体数组存像素,我用三维矩阵存像素会超时,可能跟空间局部性有关?

易错点

  1. 分块前和分块后,矩阵的下标不同,务必注意。由于都是题目给出的都是小样例,你很难发现通过题目的样例发现自己的错误!
  2. 各种转码操作不能错
  3. 逻辑理清楚

收获

  1. 能用结构体的话,别用更高维的数组

满分代码

//按要求模拟,不急不躁,一步一个脚印!

//2000*1000*3

#include <bits/stdc++.h>

using namespace std;

const int N=3e3+10,M=3010,mod=998244353;

#define pb push_back
#define se second
#define fi first

typedef long long LL;
typedef pair<LL,LL> PII;

typedef struct Rgb{
        int r,g,b;
    
}rgb;

int m,n,p,q;
rgb mat[N][M],gg[N][M];
string col;
string smod="\\x1B\\x5B\\x34\\x38\\x3B\\x32";//事先准备好的模板
string esc="\\x1B\\x5B\\x30\\x6D";
string slas="\\x6D";
string kg="\\x20";
string fh="\\x3B";
string newrow="\\x0A";
string ans;

char tochar(int x){
    if(x<10)return '0'+x;
    else return 'A'+x-10;
}


string hexcode(int x){ //获得该数的hex编码
    string s;
    int hi=x/16,lo=x%16;
    s.insert(0,1,tochar(lo));
    s.insert(0,1,tochar(hi));
    s="\\x"+s;
    return s;
}

string tohex(int x){
    
    if(x==0){ //!!!
        return hexcode('0');
    }
    vector<string>vs;
    string tts;
    while(x){
        int res=x%10;
        tts.insert(0,hexcode(res+'0'));///!!!!
        x/=10;
    }
    return tts;
}



int tocharint(char x){
    if(isdigit(x))return x-'0';
    else return 10+x-'a'; ///!!!!!!!!!!!!!!!!!!!!
}

int toint(string s){
    
    int low=tocharint(s[1]),high=tocharint(s[0]);
    return low+high*16;
}

int main(){
    
    cin>>m>>n>>p>>q;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            cin>>col;
            int len=(int)col.length()-1;
            if(len==6){
        mat[i][j].r=toint(col.substr(1,2));
        mat[i][j].g=toint(col.substr(3,2));
        mat[i][j].b=toint(col.substr(5,2));
}
            else if(len==3){
                string sr,sg,sb;
                sr.insert(0,2,col[1]);
                sg.insert(0,2,col[2]);
                sb.insert(0,2,col[3]);
				mat[i][j].r=toint(sr);
				mat[i][j].g=toint(sg);
				mat[i][j].b=toint(sb);
            }
            else{
                string sr;
                sr.insert(0,2,col[1]);
                int x=toint(sr);
               	mat[i][j].r=mat[i][j].g=mat[i][j].b=x;
            }
      
        }
    

    
    for(int i=0;i<n;i+=q){
        for(int j=0;j<m;j+=p){//枚举左上角的点
            int sr=0,sg=0,sb=0;
            for(int x=i;x<i+q;x++){
                for(int y=j;y<j+p;y++){
                    sr+=mat[x][y].r;
                    sg+=mat[x][y].g;
                    sb+=mat[x][y].b;
                    
                }
            }
            //cout<<sr<<" "<<sb<<endl;
            gg[i/q][j/p].r=sr/(p*q);
	gg[i/q][j/p].g=sg/(p*q);
gg[i/q][j/p].b=sb/(p*q);//!!!
            //cout<<g[i/q][j/p].r<<" "<<g[i/q][j/p].b<<" "<<g[i/q][j/p].b<<endl;
        }
    }
    
    int tr,tg,tb;
    int lr=0,lg=0,lb=0;//每行的背景色都是0
    for(int i=0;i<n/q;i++){
        for(int j=0;j<m/p;j++){
            rgb tmp=gg[i][j];
            
            tr=tmp.r;tg=tmp.g;tb=tmp.b;
            if(tr!=lr || tg!=lg || tb!=lb){
                if(tr==0 && tg==0 && tb==0){
                    ans+=esc;
                }
                else{ 
                    ans+=smod;
                    ans+=fh;
                    ans+=tohex(tr);
                    ans+=fh;
                    ans+=tohex(tg);
                    ans+=fh;
                    ans+=tohex(tb);
                    ans+=slas;
                }
            }
            lr=tr;
            lg=tg;
            lb=tb;
            ans+=kg; 
        }
        if(tr!=0 || tg!=0 || tb!=0)ans+=esc; //最后还原状态!!!
        lr=0;lg=0;lb=0;
        ans+=newrow;
     
    }
    
    cout<<ans;
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值