L3-004. 腫瘤診斷

L3-004. 腫瘤診斷


題目鏈接


在診斷腫瘤疾病時,計算腫瘤體積是很重要的一環。給定病竈掃描切片中標註出的疑似腫瘤區域,請你計算腫瘤的體積。

輸入格式:

輸入第一行給出4個正整數:M、N、L、T,其中M和N是每張切片的尺寸(即每張切片是一個M×N的像素矩陣。最大分辨率是1286×128);L(<=60)是切片的張數;T是一個整數閾值(若疑似腫瘤的連通體體積小於T,則該小塊忽略不計)。

最後給出L張切片。每張用一個由0和1組成的M×N的矩陣表示,其中1表示疑似腫瘤的像素,0表示正常像素。由於切片厚度可以認爲是一個常數,於是我們只要數連通體中1的個數就可以得到體積了。麻煩的是,可能存在多個腫瘤,這時我們只統計那些體積不小於T的。兩個像素被認爲是“連通的”,如果它們有一個共同的切面,如下圖所示,所有6個紅色的像素都與藍色的像素連通。


Figure 1

輸出格式:

在一行中輸出腫瘤的總體積。

輸入樣例:
3 4 5 2
1 1 1 1
1 1 1 1
1 1 1 1
0 0 1 1
0 0 1 1
0 0 1 1
1 0 1 1
0 1 0 0
0 0 0 0
1 0 1 1
0 0 0 0
0 0 0 0
0 0 0 1
0 0 0 1
1 0 0 0
輸出樣例:
26



題目看懂了其實就是搜索的一種變形,以前接觸的多的是平面內的搜索,但是這道題加入了“立體”的概念,也就是在多個疊加的平面內搜索。在平面搜搜索的時候我們會給每個座標點加上上下左右四個方向。那現在是立體搜索,那麼就有 6 個方向,即多了上一層的相同橫縱座標點和下一層的相同橫縱座標點。說白了就是平面內的搜索有 x 和 y 兩個座標軸,現在變成了立體的搜索,那就要加上一個座標軸即 z 軸。原來只有 x, y 座標軸的時候有 4 個 方向,現在加上 z 軸就有 6 個方向。值的注意的是如果使用 dfs(深度優先搜索)是會段錯誤的,應該是遞歸層次太深,程序棧內存不足了。

第一份代碼是直接用 dfs 做的,但是最後兩個測試點段錯誤

// <!! dfs 搜索,最後兩個測試點段錯誤,應該是遞歸層數太深爆棧了 !!> 
#include <iostream>
using namespace std;

int m, n, l, t;
int area[65][1300][130];
bool visit[65][1300][130];
// 一個座標點的立體周圍 6 個方向: z, x, y 
int directions[6][3] = {{0, -1, 0}, {0, 1, 0}, {0, 0, -1},
                         {0, 0, 1}, {1, 0, 0}, {-1, 0, 0}}; 

int dfs(int z, int x, int y) {
    int res = 0;
    if (z < 0 || z >= l || x < 0 || x >= m || y < 0 || y >= n || 
        !area[z][x][y] || visit[z][x][y]) {
            return res;
    }
    res++;
    visit[z][x][y] = true;
    int nextZ, nextX, nextY;
    for (int i = 0; i < 6; i++) {
        nextZ = z + directions[i][0];
        nextX = x + directions[i][1];
        nextY = y + directions[i][2];
        res += dfs(nextZ, nextX, nextY);
    }
    return res;

}

int main() {
    cin >> m >> n >> l >> t;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                scanf("%d", area[i][j] + k);
            } 
        }
    }
    int res = 0;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                if (area[i][j][k] && !visit[i][j][k]) {
                    int currentRes = dfs(i, j, k);
                    if (currentRes >= t) {
                        res += currentRes;
                    }
                }
            } 
        }
    }

    cout << res << endl;

    return 0;
}

後來想想,用了一種相對較複雜的方法,先在一層進行 dfs 搜索,然後利用這一層的搜索結果逐漸向上層搜索並保存結果。這種方法沒有爆棧,但是最一個測試點答案錯誤,沒想通哪裏錯了。。。還是貼一下代碼吧,有興趣的可以看一下思路:

// <!! 單平面 dfs + 層數循環,dfs 倒是不爆棧,但是最後一個測試點謎之答案錯誤。。。!!>
#include <iostream>
#include <vector>
using namespace std;

typedef struct Point {
    Point(int r, int c) {
        row = r, col = c;
    }
    int row;
    int col;
} Point;

int m, n, l, t;
int area[65][1300][130];
bool visit[65][1300][130];
vector<Point> points;
int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四個方向 

int dfs(int a[][130], bool visit[][130], int r, int c) {
    if (r < 0 || r >= m || c < 0 || c >= n || visit[r][c] || !a[r][c]) {
        return 0;
    }
    int res = 1;
    points.push_back(Point(r, c));
    visit[r][c] = true;
    int nextR, nextC;
    for (int i = 0; i < 4; i++) {
        nextR = r + directions[i][0];
        nextC = c + directions[i][1];
        res += dfs(a, visit, nextR, nextC);
    }
    return res;
}

int getVolume() {
    int res = 0;
    // 這個循環的含義是:獲取從第 i 層平面到最頂層平面的所有體積不小於閥值的腫瘤塊的體積。
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                if (area[i][j][k] == 1 && !visit[i][j][k]) {
                    int currentRes = 0;
                    points.clear();
                    // 儲存當前平面搜索的結果
                    currentRes += dfs(area[i], visit[i], j, k);
                    vector<Point> v = points;
                    // 向上層遍歷 
                    for (int ii = i+1; ii < l; ii++) {
                        points.clear();
                        // 如果下一層沒有找到腫瘤點,那麼這塊腫瘤體積已經確定 
                        if (v.size() == 0) {
                            break;
                        }
                        // 遍歷當前層所有和下層相同座標的點,查找腫瘤點 
                        for (int jj = 0; jj < v.size(); jj++) {
                            if (area[ii][v[jj].row][v[jj].col] &&
                                !visit[ii][v[jj].row][v[jj].col]) {
                                currentRes += dfs(area[ii], visit[ii], v[jj].row, v[jj].col);
                            }
                        }
                        v = points; 
                    }
                    // 比較腫瘤體積閥值決定是否保存該腫瘤塊體積 
                    if (currentRes >= t) {
                        res += currentRes;
                    }
                }
            }
        }
    }
    return res;
}

int main() {
    cin >> m >> n >> l >> t;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                scanf("%d", area[i][j] + k);
            } 
        }
    }

    cout << getVolume() << endl;

    return 0;
} 

最後想起來還可以用 bfs(寬度優先搜索)。爲什麼之前沒想起來。。。我應該是活在夢裏。。。

// 最後 bfs 
#include <iostream>
#include <queue>
using namespace std;

typedef struct Point {
    Point(int zz, int xx, int yy):z(zz), x(xx), y(yy) {}
    int z;
    int x;
    int y;
} Point;
int m, n, l, t;
int area[65][1300][130];
bool visit[65][1300][130];
// 一個座標點的立體周圍 6 個方向: z, x, y 
int directions[6][3] = {{0, -1, 0}, {0, 1, 0}, {0, 0, -1},
                         {0, 0, 1}, {1, 0, 0}, {-1, 0, 0}}; 

int bfs(int z, int x, int y) {
    int res = 0;
    if (z < 0 || z >= l || x < 0 || x >= m || y < 0 || y >= n || 
        !area[z][x][y] || visit[z][x][y]) {
            return res;
    }
    queue<Point> points;
    points.push(Point(z, x, y));
    visit[z][x][y] = true;
    int nextZ, nextX, nextY;
    while (!points.empty()) {
        Point p = points.front();
        points.pop();
        res++;
        for (int i = 0; i < 6; i++) {
            nextZ = p.z + directions[i][0];
            nextX = p.x + directions[i][1];
            nextY = p.y + directions[i][2];
            if (nextZ < 0 || nextZ >= l || nextX < 0 || nextX >= m || nextY < 0 || nextY >= n || 
                !area[nextZ][nextX][nextY] || visit[nextZ][nextX][nextY]) {
                continue;
            }

            visit[nextZ][nextX][nextY] = true;
            points.push(Point(nextZ, nextX, nextY));
        }
    }
    return res;
}

int main() {
    cin >> m >> n >> l >> t;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                scanf("%d", area[i][j] + k);
            } 
        }
    }
    int res = 0;
    for (int i = 0; i < l; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < n; k++) {
                if (area[i][j][k] && !visit[i][j][k]) {
                    int currentRes = bfs(i, j, k);
                    if (currentRes >= t) {
                        res += currentRes;
                    }
                }
            } 
        }
    }

    cout << res << endl;

    return 0;
}


這份代碼就 AC 了,只能說 BFS 大法好。。。

發佈了115 篇原創文章 · 獲贊 283 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章