最近准备参加csp认证,做了几套题,放假闲的无聊写一下解析。
一般csp认证的第三道大题都是小模拟,主要考验我们做题者的阅读能力(最重要的!!!)以及敲代码的熟练程度,基本不会涉及到算法,适合我这种不会算法的小弱鸡。
本题不需要任何算法,纯纯的模拟
虽然说不需要算法基础,但我们得先了解题干中的一个基础概念——小端序。以十进制为例1324的小端序为4231,简单来说小端序就是正常顺序(大端序)倒过来。
由于本题给的输入数据都是1个字节(2位16进制数),但处理时都是按2进制数进行处理,所以我们需要先构建一个将16进制数转化为2进制的函数
int six[20],num=1; // num表示已经处理到第几个字节
void change(int n){
for(int i=0;i<=19;i++) six[i]=0;
int x,y;
if(a[2*n-1]>='0'&&a[2*n-1]<='9') x=a[2*n-1]-'0';
else x=a[2*n-1]-'a'+10;
if(a[2*n]>='0'&&a[2*n]<='9') y=a[2*n]-'0';
else y=a[2*n]-'a'+10;
for(int i=4;i>=1;i--){
if(x%2==1)
six[i]=1;
else six[i]=0;
x/=2;
}
for(int i=8;i>=5;i--){
if(y%2==1)
six[i]=1;
else six[i]=0;
y/=2;
}
num++;
}
依题意,被压缩的部分分为引导域和数据域,我们需要先解压所给数据的引导域。
引导域分布在输入数据的最前几位,如果最高位为1就一直接着循环,直到找到最高位为0的第一个字节。由于数据的第一位用做判断引导域是否结束,所以每个字节只有后7位参与到引导域大小的计算。
int n=0; //n表示解压后数据应有的长度
for(int i=1;;i++){
change(i);
if(six[1]==1){
n+=getnum(2,8)*pow(128,i-1);
}
else if(six[1]==0){
n+=getnum(2,8)*pow(128,i-1);
break;
}
}
处理完引导域后我们便得知了解压后数据应有的长度,可以接着对后面的数据域进行处理。
数据域中的字节分为字面量以及回溯引用两种,其中回溯引用也分为两种不同的情况。判断这三种不同字节的方式是将它们转换为2进制后看第七位以及第八位的值。由于每个字节的后两位参与判断种类,所以只有前6位会参与运算。
由于需要进行回溯应用的输出(可以理解为输出已经已经输出过的重复数据),所以我们需要对已经输出的数据进行存储
char a[4000005],pri[4000005]; //a为存储的输入数据,pri存储已经输出过的数据
当第7位==0&&第8位==0时,这个字节为字面量,计算出字面量后要直接进行输出。
if(six[7]==0&&six[8]==0){
int l=getnum(1,6);
if(l<60) nu=l+1;
else{
l-=59;
for(int i=1;i<=l;i++){
change(num);
nu+=getnum(1,8)*pow(256,i-1);
}
nu++;
}
for(int i=num;i<=num+nu-1;i++){
printf("%c%c",a[2*i-1],a[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=a[2*i-1];
pri[2*print]=a[2*i];
}
num+=nu;
}
第7位==0&&第8位==1以及第7位==1&&第8位==0(回溯引用的两种情况),这种情况写需要先计算出偏移量o以及长度l后再输出
计算过程:
else if(six[7]==0&&six[8]==1){
l=4;
l+=getnum(4,6);
o+=getnum(1,3)*256;
change(num);
o+=getnum(1,8);
}
else if(six[7]==1&&six[8]==0){
l=1;
l+=getnum(1,6);
change(num);
o+=getnum(1,8);
change(num);
o+=getnum(1,8)*256;
}
输出回溯引用数据:
if(l!=0&&o!=0){
if(o>=l){
int begin=print;
for(int i=begin-o+1;i<=begin-o+l;i++){
printf("%c%c",pri[2*i-1],pri[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*i-1];
pri[2*print]=pri[2*i];
}
}
else{
int begin=print;
for(int i=1;i<=l/o;i++){
for(int j=begin-o+1;j<=begin;j++){
printf("%c%c",pri[2*j-1],pri[2*j]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*j-1];
pri[2*print]=pri[2*j];
}
}
for(int i=begin-o+1;i<=begin-o+l%o;i++){
printf("%c%c",pri[2*i-1],pri[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*i-1];
pri[2*print]=pri[2*i];
}
}
}
整体答案:
#include<bits/stdc++.h>
using namespace std;
int s;
char a[4000005],pri[4000005];
int six[20],num=1,print=0;
void change(int n){
for(int i=0;i<=19;i++) six[i]=0;
int x,y;
if(a[2*n-1]>='0'&&a[2*n-1]<='9') x=a[2*n-1]-'0';
else x=a[2*n-1]-'a'+10;
if(a[2*n]>='0'&&a[2*n]<='9') y=a[2*n]-'0';
else y=a[2*n]-'a'+10;
for(int i=4;i>=1;i--){
if(x%2==1)
six[i]=1;
else six[i]=0;
x/=2;
}
for(int i=8;i>=5;i--){
if(y%2==1)
six[i]=1;
else six[i]=0;
y/=2;
}
num++;
}
int getnum(int l,int r){
int num=0;
for(int i=l;i<=r;i++){
num*=2;
num+=six[i];
}
return num;
}
int main(){
scanf("%d",&s);
for(int i=1;i<=s/8;i++){
for(int j=1;j<=8;j++){
scanf(" %c%c",&a[num],&a[num+1]);
num+=2;
}
}
for(int i=1;i<=s%8;i++){
scanf(" %c%c",&a[num],&a[num+1]);
num+=2;
}
num=1;
int n=0;
for(int i=1;;i++){
change(i);
if(six[1]==1){
n+=getnum(2,8)*pow(128,i-1);
}
else if(six[1]==0){
n+=getnum(2,8)*pow(128,i-1);
break;
}
}
while(print!=n){
change(num);
int nu=0,l=0,o=0;
if(six[7]==0&&six[8]==0){
int l=getnum(1,6);
if(l<60) nu=l+1;
else{
l-=59;
for(int i=1;i<=l;i++){
change(num);
nu+=getnum(1,8)*pow(256,i-1);
}
nu++;
}
for(int i=num;i<=num+nu-1;i++){
printf("%c%c",a[2*i-1],a[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=a[2*i-1];
pri[2*print]=a[2*i];
}
num+=nu;
}
else if(six[7]==0&&six[8]==1){
l=4;
l+=getnum(4,6);
o+=getnum(1,3)*256;
change(num);
o+=getnum(1,8);
}
else if(six[7]==1&&six[8]==0){
l=1;
l+=getnum(1,6);
change(num);
o+=getnum(1,8);
change(num);
o+=getnum(1,8)*256;
}
if(l!=0&&o!=0){
if(o>=l){
int begin=print;
for(int i=begin-o+1;i<=begin-o+l;i++){
printf("%c%c",pri[2*i-1],pri[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*i-1];
pri[2*print]=pri[2*i];
}
}
else{
int begin=print;
for(int i=1;i<=l/o;i++){
for(int j=begin-o+1;j<=begin;j++){
printf("%c%c",pri[2*j-1],pri[2*j]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*j-1];
pri[2*print]=pri[2*j];
}
}
for(int i=begin-o+1;i<=begin-o+l%o;i++){
printf("%c%c",pri[2*i-1],pri[2*i]);
print++;
if(print%8==0) printf("\n");
pri[2*print-1]=pri[2*i-1];
pri[2*print]=pri[2*i];
}
}
}
}
return 0;
}
感觉输出可以单独写一个函数,这样能少敲几十行,懒得改了。。。。。。