刘汝佳《算法竞赛入门经典(第二版)》习题(八)

来源:互联网 发布:galgame汉化软件 编辑:程序博客网 时间:2024/06/04 23:32

刘汝佳《算法竞赛入门经典(第二版)》第四章习题(4-7~4-8)

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

书上的翻译较简,加上对RAID技术不了解(计算机组成原理没好好学),看了半天没看懂,还是直接看原题的英文描述比较好。

原题描述

RAID (Redundant Array of Inexpensive Disks) is a technique which uses multiple disks to store data. By storing the data on more than one disk, RAID is more fault tolerant than storing data on a single disk. If there is a problem with one of the disks, the system can still recover the original data provided that the remaining disks do not have corresponding problems.

One approach to RAID breaks data into blocks and stores these blocks on all but one of the disks. The remaining disk is used to store the parity information for the data blocks. This scheme uses vertical parity in which bits in a given position in data blocks are exclusive ORed to form the corresponding parity bit. The parity block moves between the disks, starting at the first disk, and moving to the next one in order. For instance, if there were five disks and 28 data blocks were stored on them, they would be arranged as follows:


With this arrangement of disks, a block size of two bits and even parity, the hexadecimal sample data 6C7A79EDFC (01101100 01111010 01111001 11101101 11111100 in binary) would be stored as:


If a block becomes unavailable, its information can still be retrieved using the information on the other disks. For example, if the first bit of the first block of disk 3 becomes unavailable, it can be reconstructed using the corresponding parity and data bits from the other four disks. We know that our sample system uses even parity:

0 0? 1 0 = 0

So the missing bit must be 1.

An arrangement of disks is invalid if a parity error is detected, or if any data block cannot be

reconstructed because two or more disks are unavailable for that block.

Write a program to report errors and recover information from RAID disks.

样例输入和输出



题解

这道题准确理解RAID技术的原理、异或以及输入的数据的含义至关重要。输入的第一部分包含三个数据,第一个数据d是磁盘集包含的磁盘数量,第二个数据是每一个数据块的大小(单位为比特),第三个数据是每一个磁盘中数据块和校验块的总数。输入的第二部分只有一个数据——奇偶校验,所谓奇偶校验就是一组二进制数据中“1”的数量是奇数还是偶数来校验,例如有5个比特的一组数据为11001,校验位为1比特(在这里只讨论单向校验),若采用奇校验,则数据和校验位中“1”的数量必须为奇数,数据11001一共有31,则校验位就为0才能满足条件。偶校验与奇校验原理相同,结果相反。输入的第三部分包含d行,每一行都是一个磁盘中数据块和校验块的内容。本题的RAID技术就是按照标准往数据中插入校验位,放在若干个盘中,这样即使某一个盘的某些数据损坏了也能通过其他盘的信息(每d-1个数据块就有一个校验块,使得每d个数据块的异或结果为全0或全1)恢复损坏的数据。

以第三组样例输入为例:

3 5 1

O

11111

11xxx

x1111

过程如下:

输入的第一部分3 5 1说明磁盘集有3个磁盘,每一个数据块的大小为5比特,每一个磁盘的数据块与校验块的总数为1。因此可以初步建立出一张二维表:


根据奇偶校验和RAID技术原理恢复数据(当同一数据块中出现多个损坏数据则无法恢复),每d-1个数据块就有一个校验块,找出校验块,然后将数据部分从二进制转换为十六进制输出。


源代码

#include <iostream>#include <cstdio>#include <string>int d, s, b;char type;bool check (std::string *data){    for (int i = 0; i < s*b; i++)    {        int xsum = 0, sum1 = 0, x = 0;        for (int j = 0; j < d; j++)        {            if (data[j][i] == '1')                sum1++;            if (data[j][i] == 'x')            {                xsum++;                if (xsum > 1)                    return false;                x = j;            }        }        sum1 %= 2;        if (xsum == 0)        {            if (type == 'E' && sum1 == 1)                return false;            else if (type == 'O' && sum1 == 0)                return false;        }        else if (xsum == 1)        {            if (sum1 == 1 && type == 'E')                data[x][i] = '1';            else if (sum1 == 1 && type == 'O')                data[x][i] = '0';            else if (sum1 == 0 && type == 'E')                data[x][i] = '0';            else if (sum1 == 0 && type == 'O')                data[x][i] = '1';        }    }    return true;}void out_data(std::string *data){    int sum = 0, bit_cnt = 0;    for(int i = 0; i < b; ++i)    {        int except = i % d;        for(int j = 0; j < d; ++j)        {            if(j == except)                continue;            for(int k = i * s; k < i * s + s; ++k)            {                bit_cnt = (bit_cnt + 1) % 4;                if(data[j][k] == '0')                    sum *= 2;                else                    sum = sum * 2 + 1;                if(!bit_cnt)                {                    printf("%X", sum);                    sum = 0;                }            }        }    }    if(bit_cnt)    {        int over = 4 - bit_cnt;        sum = sum * (1<<over);        printf("%X", sum);    }    printf("\n");}int main (void){    using std::cin;    using std::cout;    using std::endl;    using std::string;    int num = 0;    while (cin >> d && d)    {        num++;        int i;        cin >> s >> b >> type;        string *data = new string[b];        for (i = 0; i < d; i++)            cin >> data[i];        if (!check(data))            cout << "Disk set " << num << " is invalid.\n";        else        {            cout << "Disk set " << num << " is valid, " << "contents are: ";            out_data(data);        }        delete []data;    }    return 0;}

运行结果



习题4-8特别困的学生(Extraordinarily Tired StudentsACM/ICPC Xi‘an 2006UVa12106

课堂上有n个学生(n≤10)。每个学生都有一个睡眠-清醒周期,其中第i个学生处在它的周期的第Ci分钟。每个学生在临睡前会察看全班睡觉人数是否严格大于清醒人数,只有这个条件满足才睡觉,否则就坚持听课Ai分钟后再次检查这个条件。问经过多长时间后全班都清醒。

原题描述

When a student is too tired, he can’t help sleeping in class, even if his favorite teacher is right here in front of him. Imagine you have a class of extraordinarily tired students, how long do you have to wait, before all the students are listening to you and won’t sleep any more? In order to complete this task, you need to understand how students behave.

When a student is awaken, he struggles for a minutes listening to the teacher (after all, it’s too bad to sleep all the time). After that, he counts the number of awaken and sleeping students (including himself). If there are strictly more sleeping students than awaken students, he sleeps for b minutes. Otherwise, he struggles for another a minutes, because he knew that when there is only very few sleeping students, there is a big chance for them to be punished! Note that a student counts the number of sleeping students only when he wants to sleep again.


Now that you understand each student could be described by two integers a and b, the length of awaken and sleeping period. If there are always more sleeping students, these two periods continue again and again. We combine an awaken period with a sleeping period after it, and call the combined period an awaken-sleeping period. For example, a student with a = 1 and b = 4 has an awaken-sleeping period of awaken-sleeping-sleeping-sleeping-sleeping. In this problem, we need another parameter c (1 ≤ c ≤ a + b) to describe a student’s initial condition: the initial position in his awaken-sleeping period. The 1st and 2nd position of the period discussed above are awaken and sleeping, respectively.

Now we use a triple (a, b, c) to describe a student. Suppose there are three students (2, 4, 1), (1, 5, 2) and (1, 4, 3), all the students will be awaken at time 18. The details are shown in the table below.

Write a program to calculate the first time when all the students are not sleeping.

Input

The input consists of several test cases. The first line of each case contains a single integer n (1 ≤ n ≤ 10), the number of students. This is followed by n lines, each describing a student. Each of these lines contains three integers a, b, c (1 ≤ a, b ≤ 5), described above. The last test case is followed by a single zero, which should not be processed.

Output

For each test case, print the case number and the first time all the students are awaken. If it’ll never happen, output ‘-1’.

题解

用一个结构体存储一个学生的所有数据,模拟学生状态随时间的变化情况,以一个操作周期为单位递增,不断往后模拟,直到找出满足终止条件的时间或达到循环上限为止。模拟可以按照原题给出的图来建立一个状态表,横轴代表时间,纵轴代表学生,格子为对应学生的状态,这样的话如果某一列的状态都是清醒的,那对应的横坐标即为所求时刻,若遍历完整张表都没有,则输出-1。这种方法比较直观,缺点是占用空间较大。我采用的方法是像开头讲的,对每个学生都进行一次状态更新操作,以这一次为一个操作周期,操作一个周期后就进行一次判断,虽然比较抽象但优点在节省内存。

源代码

#include <iostream>struct Student{    int a, b, c, len;    bool sleep;};int get_awake (int m, Student *stu){    int i, num = 0;    //统计当前清醒的学生人数    for (i = 1; i <= m; i++)        if (!stu[i].sleep)            num++;    if (2*num < m)    {        for (i = 1; i <= m; i++)        {            stu[i].c = (stu[i].c+1)%stu[i].len;//更新在周期中的位置            if (stu[i].c == 0)                stu[i].c = stu[i].len;            if (stu[i].c > stu[i].a)                stu[i].sleep = true;            else                stu[i].sleep = false;        }    }    else    {        for (i = 1; i <= m; i++)        {            stu[i].c = (stu[i].c+1)%stu[i].len;            if (stu[i].c == 0)                stu[i].c = stu[i].len;            if (stu[i].c > stu[i].a)                stu[i].sleep = true;            else                stu[i].sleep = false;            //当全班清醒人数小于睡着人数时,将要进入睡眠状态的同学必须醒着            if (stu[i].c == stu[i].a+1)            {                stu[i].sleep = false;                stu[i].c = 1;            }        }    }    return num;}int main (void){    using std::cin;    using std::cout;    using std::endl;    int n, i, a, b, c, num = 0, awake = 0;    while (cin >> n && n)    {        num++;        Student *p = new Student[n+1];        for (i = 1; i <= n; i++)        {            cin >> a >> b >> c;            p[i].a = a, p[i].b = b, p[i].c = c, p[i].len = a+b;            if (c > a)                p[i].sleep = true;        }        for (i = 1; i < 1000; i++)        {            awake = get_awake(n, p);            //cout << "awake = " << awake << endl;            if (n-awake == 0)            {                cout << "Case " << num << ": " << i << endl;                break;            }        }        if (i == 1000)            cout << "Case " << num << ": " << -1 << endl;        delete []p;    }    return 0;}

运行结果





阅读全文
0 0