2013 华为编程比赛 解决方案 1
1. 引言
2013年5月11日华为成渝赛区编程比赛落下帷幕,本人有幸参加此次编程比赛,具体学校就不说了。本人选的是第一场,中间出了些小插曲,在线服务器网站访问不了,大概弄了1 个多小时才恢复正常,也算是意味中的意外吧。题目总共3题,按往常一样,由简到难。具体结果是:AC第一题,第二题错误73%(不知道怎么算的,猜应该是测试用例覆盖率吧),第三题刚想好架构,确无时间编码,也算一点遗憾吧。赛后对第三题重新进行了解答,由于本人技术有限,可能效率上达不到各位看官的要求,但对自己来说也是一种提高。
2. 题目
2.1 磁盘数据恢复
2.1.1 题目描述:
在存储领域,RAID5是一种兼顾数据安全、成本和性能的磁盘阵列技术方案,它是把数据和校验信息均匀地分散到阵列的各个磁盘上,每个磁盘上既有数据,又有校验信息,当一个数据盘损坏时,系统可以根据同一带区(下图中的D1D2D3P1)的其他数据块和对应的校验信息来重构损坏的数据。如下所示为数据D1D2D3D4D5D6D7D8D9D10D11D12即其相应校验信息P1P2P3P4在阵列中的分布(D为数据,P为校验信息,每一列为一阵列磁盘,每一行为一带区):
D1(0x1a) D2(0x2a) D3(?) P1(0x6a)
D4(0x4a) D5(0x5a) P2(?) D6(0x6a)
D7(0x7a) P3(0x6a) D8(?) D9(0x9a)
P4(0xad) D10(0xaf) D11(?) D12(0xac)
系统已经把数据信息{D1(0x1a), D2(0x2a), D3(?), D4(0x6a), D5(0x5a), D6(0x6a), D7(0x7a), D8(?), D9(0x9a), D10(0xaf), D11(?), D12(0xac)}及其异或校验码信息{P1(0x6a), P2(?), P3(0x6a), P4(0xad)}按照RAID5技术如上依次存储在四个阵列磁盘中,但不幸的是部分磁盘数据已经损坏,如上图所示第3列(即第3块磁盘),请将其同一带区中丢失的信息恢复出来。
注意:数据校验信息是通过异或计算得来的。
例如:其中“?”表示待恢复的数据和校验码信息。在输入数据时为方便操作,“?”用0xff表示,测试用例中会保证其它正常数据不会出现0xff。以上输入信息变为{ 0x1a, 0x2a, 0xff, 0x6a, 0x4a, 0x5a, 0xff, 0x6a, 0x7a, 0x6a, 0xff, 0x9a, 0xad, 0xaf, 0xff, 0xac }
丢失数据和校验信息的恢复:D3=0x5a, P2=0x7a, D8= 0x8a, D11=0xae
2.1.2 输入:
以空格隔开的16进制数字(数据共计16个,如上图中所示规模)。
2.1.3 输出:
以空格隔开的恢复后的数据和校验信息。
2.1.4 样例输入:
0x1a 0x2a 0xff 0x6a 0x4a 0x5a 0xff 0x6a 0x7a 0x6a 0xff 0x9a 0xad 0xaf 0xff 0xac
2.1.5 样例输出:
0x5a 0x7a 0x8a 0xae
2.1.6 代码思路
此题比较简单,就是以行为一带区单位进行异或操作。建立一个二维数组存储输入的RAD5数据,遍历每一行元素,判断其是否为0xff,如果是表明需要校验,校验方法:将此行另外三个元素进行异或操作,异或结果就是所求第一行的校验结果,依此类推,求取其它几行的校验数据。
在编程中如果在数据输入时就判断oxff的位置,可以缩短整体运行时间,避免Time Out of Limit.具体做法是另外建立一个一维数组,存储每一行的oxff所在列的位置。
2.2 题目:容量转换
2.2.1 题目描述:
随着大数据时代的到来,也就意味着越来越多的数据需要被存储与分析。10年前,市场上主流的硬盘容量也就20GB,而今天,硬盘容量已达到TB级别。在一些大型企业的数据中心,购买的存储容量多达几十,上百个PB,甚至达到EB级别。但用户在使用过程中,如果将EB级的容量使用MB呈现,将带来极大的困扰,比如华为网盘给你分配了50M的空间,而你仅使用了11M,还剩下39M,如果华为告诉你,你的网盘空间剩余33936KB或0.000037TB,你将很难认知当前你的网盘空间只有39M了。但如果转换到GB,则剩余就应该是0.038G,虽能够较好的感知容量剩余大小,但此时造成了容量损失为39MB-38.92MB=0.08MB,此种转换不可取,因此显示为39M为最优的容量(既能有效识别容量大小,也能够尽可能减少容量精度损失)。
因此,需要完成一个容量转换算法:给定一个在KB~EB范围内的任意容量大小,需要将其转换到(1~1024)范围内且合适的容量单位上进行呈现,要求:
1) 转换后容量的精度保留3位小数,且精度位需要下取整以保证转换后的容量一定可用;如3.1256,精度位下取整后,数字为3.125。
2) 如果容量转换后输出为0.000,则始终以整数形式输出,结果应为0GB;其余结果都需要保留3位精度。
2.2.2 输入:
容量输入格式=容量数字+空格+单位,如2.536 PB;单位有KB、MB、GB、TB、PB、EB。
2.2.3 输出:
转换后容量输出格式=容量数字+单位,如2.536PB;单位有KB、MB、GB、TB、PB、EB;
2.2.4 样例l输入:
0.00335 TB
2.2.5 样例输出:
3.430GB
2.2.6 提示
1EB=1024PB
=1024*1024TB
=1024*1024*1024GB
=1024*1024*1024*1024MB
=1024*1024*1024*1024*1024KB
2.2.7 编程思路
此题主要思路是将给定的容量转换成在1-1024范围内合适的容量,且对精度有做要求,这点很坑爹,总共只有不到10个人AC这题,看来都栽在浮点精度处理上了。我的思路是先将给定浮点乘以1000强制转换成int型,然后再转换成浮点型再除以1000,得到所要精度数据。但结果还是有错误,看来极限条件还是未考虑到,比如4.9999999 GB, 处理之后为5.000GB,以实际想要的结果4.999不符,主要是因为达到浮点的精度范围上限(float是6位,double是15位),所以导致出错。针对这种情况,需要专门进行讨论,再此不作考虑。只把总体思路表现出来。
总体思路就是考虑三种情况:
第一种情况是输入容量在1-1024范围内,此时直接按精度要求输出容量即可,如果不考虑极限情况,就是(float)((int)(num*1000))/1000;
第二种情况是输入容量小于1且大于0,则此时需要做乘运算,基准是1024,将输入容量一直乘以1024直至所乘结果在[1,1024)范围内(不包括1024),则停止乘运算,按第一种情况精度要求处理输出;
第三种情况是输入容量大于1024,则此时需要做除运算,基准也是1024,将输入容量一直除以1024直至所除结果在[1,1024)范围内(不包括1024),则停止除运算,并考虑是否为0,则按题目要求输出相应格式的容量,按第一种情况精度要求处理输出。
2.3 题目:判断给定数是否满足给定条件要求
2.3.1 题目描述:
说明:
1、 条件是一个字符串,其格式由数学上的“开闭区间”,"&&"和"||"组成。其格式为:[5,7]&&(6,9]||(10,20),该条件表示“大于等于5,小于等于7”并且“大于6,小于9”或者“大于10,小于20”。
2、 &&优先级高于||。
2.3.2 输入:
1、 字符串1:上述描述格式的条件:如[5,7]&&(6,9]||(10,20)
2、 数字:判断是否满足条件的数字:9
说明:上诉两个参数是在一行中输入的,其格式为:字符串1+空格+数字。如[1,2]||(3,4) 3
2.3.3 输出:
如果满足条件,则输出1,否则0。
2.3.4 样例输入:
[1,2]||(3,4] 3
2.3.5 样例输出:
0
2.3.6 代码思路:
此题当时想到用栈来解决,但具体怎么操作还是没细作考虑。在此描述下此题大致思路。
在想到此题时,很容易联想到求取表达式值的应用,在该应用中也是用栈来表示运算的优先级。在这题中,也同样,栈可以用来判断&&,||操作的优先级。具体思路为:用到四个栈,分别存储运算符,数字. 代码中用soper1,soper2,snum,sresult表示。soper1存储:'[',']','(',')'; soper2存储:'&','|';snum存储数字;sresult存储实时操作运算结果。
在运算过程中,soper1中始终只存储一个字符:'['或')',因为在我的思路中是实时进行范围结果判断,即只要遍历到字符为']'或')'时,即取出snum中的两个数进行判断,soper1将'['或')'出栈(出栈后为空,等待下一个'['或')'入栈), 并把判断结果放入栈sresult.
具体操作过程中看代码描述,其实整个过程只要自己亲自将运算优先级过程用笔画出来编代码就不是很难。虽然我的代码看起来有点繁琐,希望大家能提出更好的思路,欢迎拍砖!
3. 附代码:
3.1 题目1代码:
#include <iostream> using namespace std; int rad5[4][4]; int main() { int i=0,j=0,k=0; int sum[4]={0}; int flag[4]={0}; for(i=0;i<4;++i) for(j=0;j<4;++j) { scanf("%x",&rad5[i][j]); if(rad5[i][j]==0xff) { flag[i]=j; } } for(i=0;i<4;++i) { for(k=0;k<4;++k) if(k!=flag[i]) sum[i]^=rad5[i][k]; } for(i=0;i<4;++i) { printf("%#0x ",sum[i]); } printf("\n"); return 0; }
3.2 题目2代码:
#include <iostream> #include <cmath> #include <limits> using namespace std; const int N=6; char ua[N][3]={"KB","MB","GB","TB","PB","EB"}; typedef std::numeric_limits<float> dbl; int main() { char unit[3]; float c; int i=0,flag=0; while(scanf("%f%s",&c,unit)!=EOF) { for(i=0;i<N;++i) { if(!strcmp(unit,ua[i])) { flag=i; if(c>=1 && c<1024) { c=(float)((int)(c*1000))/1000; if(c==0) printf("%.0f%s\n",c,unit); else printf("%.3f%s\n",c,unit); } else if(c<1) { while(c<1&&c>0) { c=c*1024; if(flag>0) flag--; } c=(float)((int)(c*1000))/1000; if(c==0) printf("%.0f%s\n",c,ua[flag]); else printf("%.3f%s\n",c,ua[flag]); } else if(c>1023) { while(c>1023) { c=c/1024; if(flag<N) flag++; } c=(float)((int)(c*1000))/1000; if(c==0) printf("%.0f%s\n",c,ua[flag]); else printf("%.3f%s\n",c,ua[flag]); } } } } return 0; }
3.3 题目3代码
#include <iostream> #include <stack> #include <string.h> using namespace std; const int N=1005; int judgePri(char c1, char c2) { if(c1==c2) return 0; if(c1=='&' && c2=='|') return 1; if(c1=='|' && c2=='&') return -1; } int judgeNum(char *pstr,int num) { stack<char> soper1,soper2; //soper1: [,],(,); soper2: &, | stack<int> snum,sresult; //snum: number stack, sresult: result stack int i,len; int result=0; len=strlen(pstr); for(i=0;i<len;++i) { if(pstr[i]>='0' && pstr[i]<='9') //if char is number { int k=i,j=0; char cnum[N]; while(isdigit(pstr[k])) //for continous number in pstr,then convert str to int { cnum[j++]=pstr[k]; k++; } cnum[j]='\0'; //terminate the number string i=k-1; snum.push(atoi(cnum)); //push into number stack cout<<"Elem of snum: "<<pstr[i]<<" push into stack"<<endl; } else if(pstr[i]=='[' || pstr[i]=='(') { soper1.push(pstr[i]); cout<<"Elem of soper1: "<<pstr[i]<<" push into stack"<<endl; } else if(pstr[i]=='&' || pstr[i]=='|') { if(!soper2.empty()) { int ret=judgePri(soper2.top(),pstr[i]); //judge priviledge int tr1,tr2,tr; char tc; if(-1 == ret) //priviledge: soper2.top()<pstr[i] { soper2.push(pstr[i]); } else if(0 == ret)//priviledge: soper2.top() = pstr[i] { tr1=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr1<<" pop out stack"<<endl; tr2=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr2<<" pop out stack"<<endl; tc=soper2.top(); soper2.pop(); cout<<"Elem of soper2: "<<tc<<" pop out stack"<<endl; if(tc== '&') tr= tr1 && tr2; else tr = tr1 || tr1; sresult.push(tr); //put the newest result into sresult soper2.push(pstr[i]);// put the newest optr into soper2 cout<<"Elem of sresult: "<<tr<<" push into stack"<<endl; cout<<"Elem of soper2: "<<pstr[i]<<" push into stack"<<endl; } else //priviledge: soper2.top()>pstr[i] { while(!soper2.empty()) { tr1=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr1<<" pop out stack"<<endl; tr2=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr2<<" pop out stack"<<endl; tc=soper2.top(); soper2.pop(); cout<<"Elem of soper2: "<<tc<<" pop out stack"<<endl; if(tc == '&') tr=tr1 && tr2; else tr=tr1 || tr2; sresult.push(tr); cout<<"Elem of sresult: "<<tr<<" push into stack"<<endl; } soper2.push(pstr[i]); cout<<"Elem of soper2: "<<pstr[i]<<" push into stack"<<endl; } } else //if soper2 is empty, push directly { soper2.push(pstr[i]); cout<<"Elem of soper2: "<<pstr[i]<<" push into stack"<<endl; } i++; // only save one char for "&&" and "||" } else if(pstr[i]==']'||pstr[i]==')') { char ch; int clow,cup; ch=soper1.top(); soper1.pop(); cout<<"Elem of soper1: "<<ch<<" pop out stack"<<endl; cup=snum.top(); snum.pop(); cout<<"Elem of snum: "<<cup<<" pop out stack"<<endl; clow=snum.top(); snum.pop(); cout<<"Elem of snum: "<<clow<<" pop out stack"<<endl; if(ch=='[' && pstr[i]==']') { if(num>=clow && num <=cup) result=1; else result=0; } if(ch=='[' && pstr[i]==')') { if(num>=clow && num<cup) result=1; else result=0; } if(ch=='(' && pstr[i]==')') { if(num>clow && num<cup) result=1; else result=0; } if(ch=='(' && pstr[i]==']') { if(num>clow && num<=cup) result=1; else result=0; } sresult.push(result); cout<<"Elem of sresult: "<<result<<" push into stack"<<endl; } } //当上述字符串遍历完后,对于最后一次入栈的数字范围并未判断,所以可以推断soper2,sresult中不为空,还需进一步判断 while(!soper2.empty()) { int tr1,tr2,tr; char tc; tr1=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr1<<" pop out stack"<<endl; tr2=sresult.top(); sresult.pop(); cout<<"Elem of sresult: "<<tr2<<" pop out stack"<<endl; tc=soper2.top(); soper2.pop(); cout<<"Elem of soper2: "<<tc<<" pop out stack"<<endl; if(tc== '&') tr=tr1 && tr2; else tr=tr1 || tr2; sresult.push(tr); cout<<"Elem of sresult: "<<tr<<" push into stack"<<endl; } if(!sresult.empty()) return sresult.top(); //sresult栈顶元素即为最终结果 return 0; } int main() { char ch[N]; int n; while(scanf("%s %d",ch,&n)!=EOF) { cout<<judgeNum(ch,n)<<endl; } return 0; }