1. 程式人生 > >[刷題]演算法競賽入門經典(第2版) 4-7/UVa509

[刷題]演算法競賽入門經典(第2版) 4-7/UVa509

//UVa509 - RAID!
#include<iostream>
int d, s, b, t, times = 0;
char disk_data[7][6666], type;

inline char* disk(int x, int y, int z) {//二維陣列當作三維陣列使,方便運算
    return *(disk_data + x - 1) + (y - 1)*s + z - 1;
}
inline char* parity(int y, int z) {//定位到當前一行blocks的parity
    return *(disk_data + (y - 1) % d) + (y - 1
)*s + z - 1; } void print_data() { int f = 1, n = 0; for (int j = 1;j <= b;++j) for (int i = 1;i <= d;++i) for (int k = 1;k <= s;++k) { if (disk(i, j, k) == parity(j, k)) continue; if (f == 5) { printf("%X", n); f = 1, n = 0; } n = (n << 1
) + *disk(i, j, k) - 48; ++f; } printf("%X\n", (n << (5 - f))); } int main() { //freopen("in.txt", "r", stdin); while (scanf("%d", &d) && d != 0) { scanf("%d%d\n%c", &s, &b, &type); t = (type == 'E' ? 0 : 1); for (int i = 0;i < d;++i) scanf
("%s", disk_data[i]); for (int j = 1;j <= b;++j) for (int k = 1;k <= s;++k) {//資料讀取完畢,開始檢測 int sum = 0; bool flag = false;//0:沒有x問題;1:有一個x; char *p;//定位x的位置 for (int i = 1;i <= d;++i) { if (*disk(i, j, k) != 'x') sum += *disk(i, j, k) - '0'; else if (flag) goto o_O;//invalid:two or more disks are unavailable for that block else p = disk(i, j, k), flag = true;//出現第一個x,並定位 } if (flag) *p = (sum % 2 == t ? '0' : '1');//計算x else if (sum % 2 != t) goto o_O;//invalid:a parity error is detected } printf("Disk set %d is valid, contents are: ", ++times);//能執行完所有迴圈的才是真男人 print_data(); continue; o_O:printf("Disk set %d is invalid.\n", ++times);//我就是用goto了怎麼著!跳出三重迴圈用goto多方便 } return 0; }

題意:好幾塊硬碟組RAID,組的方法是每個硬碟都平均劃分成b個塊(block),每塊硬碟的第i個塊一起組成一個區域(也就是一塊資料平均儲存在n個硬盤裡,實際上說是儲存在n-1個硬盤裡更合理,因為其中一塊硬碟的這個區域儲存的是用於資料恢復用的,名為parity block)同時不同parity還錯開儲存在不同的硬盤裡,這樣就有效防止了某個硬碟損壞導致全盤陣列丟失。
而這個parity存的又到底是什麼呢,是這一行每個data block對應的每個bit進行某種運算得來。運演算法則可理解為,每個bit存的0或1進行相加,若結果(在我的程式碼裡用sum表示)為偶數,parity對應bit儲存為0,否則為1(even的情況。odd反過來:若為奇數儲存為0。沒什麼意義,就是個標準不同)。
然後就是校驗資料是否損壞。若直接告訴你一行裡有一個損壞(表示為x),那麼沒關係,可以通過其他那幾個盤恢復。若一行裡有倆損壞,那就沒戲了。若一行沒有出現x,那麼所有資料相加,結果若為0(E的情況。O為1)則資料正常,否則出錯。

分析:沒什麼特殊的思路,就一步步按照題目來唄。輸入的格式和題目給的圖表格式座標是反的,比較討厭。得用到三重迴圈,不同地方迴圈的順序還不一樣,比較繞,思路要理清了。然後三重迴圈跳出麻煩(因為我把其中兩個迴圈寫在了一行,這樣理解上方便,程式碼看著也清楚,所以不方便加大括號),我就用了goto。是的我就是用goto了怎麼著啦o_O!你咬我啊!在這種情況下多好用!具體演算法都在程式碼註釋裡了。
還有那個print_data()函式,那個三重迴圈裡面我本來寫的是

for (int j = 1;j <= b;++j) for (int i = 1;i <= d;++i) for (int k = 1;k <= s;++k) {
        if (f == 5) {
            printf("%X", n);
            f = 1, n = 0;
        }
        if (disk(i, j, k) == parity(j, k)) continue;
        n = (n << 1) + *disk(i, j, k) - 48;
        ++f;
}

也就是三個步驟(是否輸出、當前資料是否是parity的、是否讀入資料到n)順序不一樣。我原本的目的是除了最後一個十六進位制數字,其他均在三重迴圈裡輸出。理由是最後一個數據比較特殊,它有可能不滿4bit,要手動新增0直至其滿4bit,而且順便要輸出換行。下面這個版本有個bug,就是如果最最後一個block是parity block,那麼會多輸出一個0。原因很簡單我就不說了,但是怎麼改動我倒是想了很久,怎麼改都要加變數、加好幾個if,程式碼瞬間複雜很多,我不太情願。最後發現只需要順序倒一下即可!
果然程式設計這個東西,順序很重要啊。