CCF 201903-3损坏的RAID5

mac2025-05-06  3

题目描述

题解1

题目很长,耐心读完题目后,获取以下信息:

磁盘的数据分布单元是条带,而条带又是由多个数据块组成。题中的每两个字符构成一个16进制数,代表一个字带。一个数据块的大小是4个字节即8个字符。校验块从右向左依次选择,到达最左边后,再从最右边开始。而数据存储从校验块相邻的下一磁盘块开始。 观察子任务1,2,3,4,5都满足l = n,我们先仅考虑磁盘未损坏的情况,这时候只需要确定所读的块所在的位置就可以读取信息,不需要利用异或求解。 diskIdx, stripeIdx, checkCnt, checkPos, dataPos;//磁盘号,条带号,前面校验盘的数量,校验盘所处的位置,数据的位置 确定块的位置先要确定哪个磁盘。diskIdx = idx2 / s % n 然后确定它在磁盘的哪个条带(先不考虑校验块)。stripeIdx = idx2 / s / n; 确定该块前面有几个校验条带。 checkPos = n - 1 -diskIdx; if(stripeIdx < checkPos){ checkCnt = 0; } else{ checkCnt = (stripeIdx - checkPos) / (n - 1) + 1; }

最后确定块在磁盘的位置。dataPos = (stripeIdx + checkCnt) * s + (idx2 % s); 具体见代码1。

代码1

// // Created by Onwaier Lee on 2019-10-31. // #include <cstdio> #include <iostream> #include <cstring> #include <string> #include <algorithm> #include <vector> using namespace std; #define MAX 1005 char data[MAX][82000]; int main(){ //freopen("/home/onwaier/文档/a.txt", "r", stdin); //先处理有序的情况 int n, s, l, m, total; //n为阵列中硬盘的数目,s为阵列中条带的大小,l为现存硬盘的数目 //m为查询次数, total为阵列总长度 int idx1, idx2;//idx1为磁盘的序号, idx2为块的序号 string str;//str为磁盘的数据 scanf("%d%d%d", &n, &s, &l); for(int i = 0; i < l; ++i){ scanf("%d ", &idx1); //scanf("%s", data[idx1]); //gets(data[idx1]); fgets(data[idx1], 82000, stdin);//注意fgets的用法 //printf("%s\n", data[idx1]); } total = strlen(data[idx1]) / 8 * (n - 1); //cout << "total:" << total << endl; scanf("%d", &m); for(int i = 0; i < m; ++i){ scanf("%d", &idx2); //块号超过阵列长度 if(idx2 >= total){ cout << "-" << endl; continue; } int diskIdx, stripeIdx, checkCnt, checkPos, dataPos;//磁盘号,条带号,前面校验盘的数量,校验盘所处的位置,数据的位置 //先确定位于哪块磁盘 diskIdx = idx2 / s % n; stripeIdx = idx2 / s / n; checkPos = n - 1 -diskIdx; if(stripeIdx < checkPos){ checkCnt = 0; } else{ checkCnt = (stripeIdx - checkPos) / (n - 1) + 1; } //再确定磁盘中的哪个位置 dataPos = (stripeIdx + checkCnt) * s + (idx2 % s); for(int j = dataPos * 8; j <dataPos * 8 + 8; ++j){ printf("%c", data[diskIdx][j]); } printf("\n"); } return 0; }

代码1第一次提交时,使用的是cin,读入字符串数据,只有30%数据过了,提示超时。猜想可能是cin的原因,然后将string换成char *,用scanf读入字符串,这次有70%的数据过了,但仍然提示超时。有点想不懂了,就查看一下别人的博客。 scanf会超时是因为后面的测试点的字符串数据太大,这里要用gets(被弃用了)或者fgets。注意fgets的用法,读入的字符串的长度小于指定的长度,会自动加上\n,输入数字时要将空格接收,不然会被fgets读取。 另外一种解决超时的方法是用cin,然后关闭c++的流同步,防止cin超时。ios::sync_with_stdio(false);

用了fgets后,竟然100%数据通过,我情况还没考虑完全呢?这里说明后台数据有点问题。不信你们可以试一下。

题解2

考虑l<=n的情况,此时如果读取的磁盘块数据出现缺失,需要借助校验块来恢复。主要用到的就是异或的方法。

代码2

// // Created by Onwaier Lee on 2019-10-31. // #include <cstdio> #include <iostream> #include <cstring> #include <string> #include <algorithm> #include <vector> using namespace std; #define MAX 1005 char data[MAX][82000]; bool mark[MAX];//标记磁盘是否缺失 int toNum(char ch){ if(isdigit(ch)){ return ch -'0'; } else{ return ch - 'A' + 10; } } char toChar(int num){ if(num < 10){ return num + '0'; } else{ return 'A' + (num - 10); } } void deal(int diskIdx, int n, int idx){ if(mark[diskIdx]){ for(int j = idx; j < idx + 8; ++j){ printf("%c", data[diskIdx][j]); } printf("\n"); } else{ for(int i = idx; i < idx + 8; ++i){ int res = -1; for(int j = 0; j < n; ++j){ if(diskIdx != j){ if(res == -1){ res = toNum(data[j][i]); } else{ res = res ^ toNum(data[j][i]); } } } printf("%c", toChar(res)); } printf("\n"); } } int main(){ //freopen("/home/onwaier/文档/a.txt", "r", stdin); //先处理有序的情况 int n, s, l, m, total; //n为阵列中硬盘的数目,s为阵列中条带的大小,l为现存硬盘的数目 //m为查询次数, total为阵列总长度 int idx1, idx2;//idx1为磁盘的序号, idx2为块的序号 string str;//str为磁盘的数据 memset(mark, false, sizeof(mark)); scanf("%d%d%d", &n, &s, &l); for(int i = 0; i < l; ++i){ scanf("%d ", &idx1); //scanf("%s", data[idx1]); //gets(data[idx1]); fgets(data[idx1], 82000, stdin);//注意fgets的用法 mark[idx1] = true;//表示磁盘存在未缺失 //printf("%s\n", data[idx1]); } total = strlen(data[idx1]) / 8 * (n - 1); //cout << "total:" << total << endl; scanf("%d", &m); for(int i = 0; i < m; ++i){ scanf("%d", &idx2); //块号超过阵列长度 if(idx2 >= total){ cout << "-" << endl; continue; } int diskIdx, stripeIdx, checkCnt, checkPos, dataPos;//磁盘号,条带号,前面校验盘的数量,校验盘所处的位置,数据的位置 //先确定位于哪块磁盘 diskIdx = idx2 / s % n; if(!mark[diskIdx] && l < n - 1){//磁盘缺失且剩余磁盘不足于恢复数据 cout << "-" << endl; continue; } stripeIdx = idx2 / s / n; checkPos = n - 1 -diskIdx; if(stripeIdx < checkPos){ checkCnt = 0; } else{ checkCnt = (stripeIdx - checkPos) / (n - 1) + 1; } //再确定磁盘中的哪个位置 dataPos = (stripeIdx + checkCnt) * s + (idx2 % s); deal(diskIdx, n, dataPos * 8); // for(int j = dataPos * 8; j <dataPos * 8 + 8; ++j){ // printf("%c", data[diskIdx][j]); // } // printf("\n"); } return 0; }

github地址

最新回复(0)