小言_互联网的博客

CSP损坏的RAID5 201903-3

231人阅读  评论(0)

点击此处直达题目分析




题目分析:
这道题光是理解题意就花了好长时间,感觉出题人在写小说,但是把题目所描述的问题理清了之后,这道题编写起来并不难。

我在看题目的时候,有一个地方绕了好长时间。题目中给举了一个n=3的例子,也就是下面这张图。

这个看起来还是比较好理解的,就是图中横着的每一行算是一个组(组的概念是我自己加的,题目中并未提及此概念),每一个组中有一个校验盘(实际上一个条带),在图中是灰色的方框P,组内的其它条带都是用来存储数据的。校验条带的选取规则和数据条带的选取规则也比较好理解,虽然前面有一句话我没看懂,

怎么还用条带来存储条带?但先不管它,继续往后看到了样例2,

我被它方框里的编号给弄糊涂了,前面的图的编号是横着进行编的,怎么到了这儿变成了先竖着编再横着编了。
其实这个图中的编号是内存块在整个RAID5中的编号,而前面的图的编号是RAID5中每个条带的编号,只是为了说明条带的编号规则。
在下面的图中,我又把条带的编号给补上了,每两个内存块是一个条带。

数据结构
定义一个string类型的数组,用来存储每个磁盘的内容,因为子任务说明中n不超过1000,因此数组定义如下

string Disk[1000];

在使用这个数组时,可以把它看成如下的形式

Disk[0]存储第0个硬盘的内容,Disk[1]存储第1个硬盘的内容…因为磁盘中每一块的大小是四个字节,每两个字符占一个字节,因此Disk[0]的前八个字符可以表示第0个磁盘的第0块,第8~15个字符表示第0个磁盘的第1块,依次类推。

下面的代码是此程序的核心

	int stripeRow;	//要查询的块所在的条带的行号
	int pCol;	//校验块P所在的列号
	int stripeOrder;	//要查询的块在第几个条带上
	int rowMin;	//当前行的最小条带编号
	int col;	//要查询的块所在的磁盘
	int blockOrder;	//要查询的块在磁盘的编号

		stripeOrder = b / s;
		stripeRow = stripeOrder / (n - 1);
		pCol = n - 1 - stripeRow % n;
		rowMin = stripeRow * (n - 1);
		col = (pCol+(stripeOrder - rowMin) + 1) % n;
		blockOrder = stripeRow * s + b % s;

它的功能就是定位到要查询的块在哪个磁盘上,是某个磁盘的第几块。
我们以题目中的实例2为例来说明这段代码的执行过程,假设要找b=7位置。

b=7,s=2,所以stripeOrder=3,说明要查询的块在第三个条带,第三个条带在RAID5中所在的行号为stripeRow=1,第一行的校验块P所在的列是pCol=1,第一行编号最小的条带的编号是rowMin=2,所要找的块b=7所在的列col=0,此块是第0个磁盘的第blockOrder=3块。
注意事项
最初写完代码进行测试的时候,总是显示超时,于是又回去想办法降低时间复杂度,一通操作之后始终运行超时。最终从网上找到了解决办法,在代码里加一句ios::sync_with_stdio(false);就可以了。至于原理,是因为C++为了兼容C,所以导致cin和cout变得异常缓慢,上述语句就是禁止兼容C语言,所以输入输出就变块了。
源代码

#include <bits/stdc++.h>
using namespace std;

	string Disk[1000];
	int n, s, l;

void Inquire(int blockNum, int col, long long  & result) {
		string block;	//存放每个磁盘中某个块的内容
		long long numer;	//将字符串按照十六进制转换成数值
		bool flag = true;
		for (int i = 0; i < n; i++) {
			try{	//用try catch语句,防止Disk[i]为空时引发未经处理的异常
				block = Disk[i].substr(8 * blockNum, 8);
					numer = stoll(block,nullptr,16);
				if (flag) {
					result = numer;
					flag = false;
				}
				else {
						result = result ^ numer;
				}
			}
			catch(...){}
		}		
}
int main() {
	ios::sync_with_stdio(false);
	int m;
	int b;	//待查询的块编号
	int index;
	int length = 0;	//记录每个磁盘存了多个了字符
	cin >> n >> s >> l;
	int count = n - l;	//用来记录缺失的磁盘数
	for (int i = 0; i < l; i++) {
		cin >> index;
		cin >> Disk[index];
		if(length == 0)
		length = Disk[index].size();
	}
	int stripeRow;	//要查询的块所在的条带的行号
	int pCol;	//校验块P所在的列号
	int stripeOrder;	//要查询的块在第几个条带上
	int rowMin;	//当前行的最小条带编号
	int col;	//要查询的块所在的磁盘
	int blockOrder;	//要查询的块在磁盘的编号
	cin >> m;
	for (int i = 0; i < m; i++) {
		cin >> b;
		if (b >= (length / 8) * (n - 1))	//指定的块超出阵列的总长度
		{
			cout << "-" << "\n";
			continue;
		}

		stripeOrder = b / s;
		stripeRow = stripeOrder / (n - 1);
		pCol = n - 1 - stripeRow % n;
		rowMin = stripeRow * (n - 1);
		col = (pCol+(stripeOrder - rowMin) + 1) % n;
		blockOrder = stripeRow * s + b % s;

		if (Disk[col] != "") {	//如果待查询的磁盘存在
			cout << Disk[col].substr(8 * blockOrder, 8) << "\n";
			continue;
		}

		if (Disk[col] == "" && count >= 2) {	//待查询的磁盘不存在且缺失了两块以上的磁盘	
			cout << "-" << "\n";
			continue;	
		}
		
		long long result = 0;
		Inquire(blockOrder, col, result);	
		cout.fill('0');	
		cout << setw(8) << hex << uppercase << result<<"\n";
	}

	return 0;
}

/*
2 1 2
0 000102030405060710111213141516172081222324252627
1 000102030405060710111213141516172051222324252627
2
0
1

3 2 2
0 000102030405060710111213141516172081222324252627
1 A0A1A2A3A4A5A6A7B0B1B2B3B4B5B6B7C0C1C2C3C4C5C6C7
2
2
5
*/

收获

  • ios::sync_with_stdio(false)的使用可以加快输入输出的速度,缺点是不能再使用scanf和printf
  • 字符串转数字,转换为long long类型的
  • 对数值的二进制形式有了更深的认识

转载:https://blog.csdn.net/weixin_43074474/article/details/102485232
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场