习题4-7 RAID技术 (RAID!, ACM/ICPC World Finals 1997, UVa509)

首先要明白RAID技术的原理

RAID原理:
把数据分成多个数据块,分别存储到d个磁盘中,一个数据块的数据对应s个比特,一个磁盘存储b个数据块。在一个磁盘中数,每隔d-1个数据块有一个校验块,校验块存储的是校验数据,使得每d个数据块(在磁盘间数)的异或结果为全0(偶校验)或全1(奇校验)

举个例子:
d=5, s=2, b=7
6C7A79EDFC6C7A
(0110 1100 0111 1010 0111 1001 1110 1101 1111 1100 0110 1100 0111 1010) 的存储方式如下,红色的是校验块

(最后的两列是我另加的,题目中没有,方便大家理解校验块位置)

习题4-7 RAID技术 (RAID!, ACM/ICPC World Finals 1997, UVa509)_第1张图片
假设使用偶校验(E),用 ^ 代表异或运算(C/C++)
第一列为0 ^ 0 ^ 1 ^ 1 ^ 0 = 0,即使少一个也能根据偶校验判断出缺少的是0还是1

需要注意的是:

1.原题中的例子是一列为一个磁盘,然而实际输入是一行为一个磁盘,所以如果用二维数组存储数据,检查每一列是否满足校验就可以了
2.如果一列中丢失的数据块大于1,那么数据无法恢复
3.丢失的数据块位置随机,要考虑到可能是一列中的第一个,所以暂存异或结果的变量不能默认取第一个
4.数据要转换成大写十六进制输出,因为四个二进制位转换成一位,所以如果数据长度不能整除4,要在末尾补0

代码如下:

#include
#include

const int MAX_S = 70;
const int MAX_D = 10;
const int MAX_B = 110;

char data[MAX_D][MAX_S * MAX_B];  
char contents[MAX_D * MAX_S * MAX_B];  // 存放二进制数据 
int res[MAX_D* MAX_S * MAX_B/4]; //存放十进制数据,方便16进制输出 
int flag = 1; // 能否恢复数据 
int d, s, b;
char type;

void check(int col) {
     
	int cnt = 0, cnt_x = 0, t, index;  // cnt_x是x的个数, index是x的位置 
	int r = type == 'E' ? 0 : 1;
	for(int i = 0; i < d; i++) {
     
		if(data[i][col] == 'x') {
     
			if(cnt_x == 0) {
     
				index = i;
			} else {
     
				flag = 0;
				return;
			}
			cnt_x++;
		}
		else {
     
			if(cnt == 0) t = data[i][col] - '0'; // 一直WA卡在这了 
			else t ^= (data[i][col] - '0');
			cnt++;
		}
	}
	if(cnt_x == 1) {
     
		if((t ^ 1) == r) {
      data[index][col] = '1'; t ^= 1;}
		else {
      data[index][col] = '0'; t ^= 0;}
	}
	if(t != r || cnt_x > 1) {
     flag = 0; return;}
}

void cal_contents() {
     
	int index = 0;
	for(int i = 0; i < b; i++) {
       //列 
		for(int j = 0; j < d; j++) {
       //行 
			// 第(j, i)个block
			if(i%d == j) continue;
			for(int k = 0; k < s; k++) {
     
				contents[index++] = data[j][i * s + k];
			}
		}
	}
	while((strlen(contents)%4) != 0) {
     
		contents[index++] = '0'; 
	}
}

void cal_res() {
     
	for(int i = 0; i < strlen(contents)/4; i++) {
     
		int t = 0;
		for(int j = 0; j < 4; j++) {
     
			t = t * 2 + contents[4 * i + j] - '0';
		}
		res[i] = t;
	}
}

int main(){
     
	#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
	#endif
	int kase = 0;
	while(scanf("%d", &d)==1 && d) {
     
		scanf("%d%d\n", &s, &b);
		type = getchar();
		flag = 1;
		memset(data, '\0', sizeof(data));
		memset(contents, '\0', sizeof(contents));
		memset(res, 0, sizeof(res));
		for(int i = 0; i < d; i++) {
     
			scanf("%s", data[i]);
		}
		for(int i = 0; i < s * b; i++) {
     
			check(i); // 校验, 按位进行异或运算
			if(!flag) break;
		}
		printf("Disk set %d is ", ++kase);
		if(flag) {
     
			printf("valid, contents are: ");
			cal_contents();
			cal_res(); // 二进制转换成十进制 
			for(int i = 0; i < strlen(contents)/4; i++) printf("%X", res[i]);  // 输出十六进制 
			printf("\n");
		} else {
     
			printf("invalid.\n");
		}
	}
	return 0;
} 

你可能感兴趣的:(算法竞赛,算法,acm竞赛)