熱門文章

2025年5月17日 星期六

ZeroJudge 解題筆記:k930. P7. 興建圍籬 (Fence)

作者:王一哲
日期:2025年5月17日



ZeroJudge 題目連結:k930. P7. 興建圍籬 (Fence)

解題想法


這題我用 BFS,但是在 Python 不論是用 deque 或 list 都太慢,在 C++ 用 queue 或 vector 都要跑 0.1 s。

Python 程式碼


寫法1,用 deque 儲存待走訪的佇列,但是速度不夠快,只能通過 60% 的測資。
import sys
from collections import deque

for line in sys.stdin:
    H, W = map(int, line.split())  # 地圖 H*W
    grid = [list(map(int, input().split())) for _ in range(H)]  # 讀取地圖
    num = 1  # 花地編號
    ans = []  # 答案
    for i in range(H):  # 依序掃過每一格
        for j in range(W):
            if grid[i][j] == 1:  # 如果此格是花地
                tot = 0  # 這區的圍籬數量
                que = deque([(i, j)])  # 待走訪的佇列
                num += 1  # 編號加 1
                while que:  # 如果 que 還有資料繼續執行
                    r, c = que.popleft()  # 從 que 開頭讀取並移除資料
                    grid[r][c] = num  # 設定 grid[r][c] 的編號
                    for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):  # 四方位檢查
                        nr, nc = r+dr, c+dc  # 要檢查的格子 (nr, nc)
                        if 0 <= nr < H and 0 <= nc < W:  # 如果 nr, nc 沒有出界
                            if grid[nr][nc] == 1:  # 如果是還沒有編號的花地
                                grid[nr][nc] = num  # 設定編號
                                que.append((nr, nc))  # (nr, nc) 加入 que
                            elif grid[nr][nc] == 0:  # 如果是空地
                                tot += 1  # (r, c) 需要加一片圍籬
                ans.append(tot)  # 將 tot 加入 ans
    print(*sorted(ans))  # 將 ans 排序後印出

寫法2,用串列儲存待走訪的佇列,但是速度不夠快,只能通過 60% 的測資。
import sys

for line in sys.stdin:
    H, W = map(int, line.split())  # 地圖 H*W
    grid = [list(map(int, input().split())) for _ in range(H)]  # 讀取地圖
    num = 1  # 花地編號
    ans = []  # 答案
    for i in range(H):  # 依序掃過每一格
        for j in range(W):
            if grid[i][j] == 1:  # 如果此格是花地
                tot = 0  # 這區的圍籬數量
                que = [(i, j)]  # 待走訪的佇列
                head = 0  # 從 que 讀取資料的索引值
                num += 1  # 編號加 1
                while head < len(que):  # 如果 head 還沒有出界繼續執行
                    r, c = que[head]; head += 1  # 從 que 讀取位置,head 加 1
                    grid[r][c] = num  # 設定 grid[r][c] 的編號
                    for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):  # 四方位檢查
                        nr, nc = r+dr, c+dc  # 要檢查的格子 (nr, nc)
                        if 0 <= nr < H and 0 <= nc < W:  # 如果 nr, nc 沒有出界
                            if grid[nr][nc] == 1:  # 如果是還沒有編號的花地
                                grid[nr][nc] = num  # 設定編號
                                que.append((nr, nc))  # (nr, nc) 加入 que
                            elif grid[nr][nc] == 0:  # 如果是空地
                                tot += 1  # (r, c) 需要加一片圍籬
                ans.append(tot)  # 將 tot 加入 ans
    print(*sorted(ans))  # 將 ans 排序後印出


C++ 程式碼


寫法1,用 queue 儲存待走訪的佇列,使用時間約為 0.1 s,記憶體約為 4.9 MB,通過測試。
#include <cstdio>
#include <vector>
#include <queue>
#include <utility>
#include <algorithm>
using namespace std;

int main() {
    int dr[4] = {0, 1, 0, -1}, dc[4] = {1, 0, -1, 0};  // 四方位檢查用的位移量
    int H, W;  // 地圖 H*W
    while(scanf("%d %d", &H, &W) != EOF) {
        int grid[H][W];  // 讀取地圖
        for(int i=0; i<H; i++) {
            for(int j=0; j<W; j++) {
                scanf("%d", &grid[i][j]);
            }
        }
        int num = 1;  // 花地編號
        vector<int> ans;  // 答案
        for(int i=0; i<H; i++) {  // 依序掃過每一格
            for(int j=0; j<W; j++) {
                if (grid[i][j] == 1) {  // 如果此格是花地
                    int tot = 0;  // 這區的圍籬數量
                    queue<pair<int, int>> que; que.push(make_pair(i, j));  // 待走訪的佇列
                    num++;  // 編號加 1
                    while(!que.empty()) {  // 如果 que 還有資料繼續執行
                        int r = que.front().first, c = que.front().second;  // 從 que 開頭讀取位置
                        que.pop();  // 移除 que 開頭的資料
                        grid[r][c] = num;  // 設定 grid[r][c] 的編號
                        for(int k=0; k<4; k++) {  // 四方位檢查
                            int nr = r+dr[k], nc = c+dc[k];  // 要檢查的格子 (nr, nc)
                            if (nr >= 0 && nr < H && nc >= 0 && nc < W) {  // 如果 nr, nc 沒有出界
                                if (grid[nr][nc] == 1) {  // 如果是還沒有編號的花地
                                    grid[nr][nc] = num;  // 設定編號
                                    que.push(make_pair(nr, nc));  // (nr, nc) 加入 que
                                } else if (grid[nr][nc] == 0) {  // 如果是空地
                                    tot++;  // (r, c) 需要加一片圍籬
                                }
                            }
                        }
                    }
                    ans.push_back(tot);  // 將 tot 加入 ans
                }
            }
        }
        sort(ans.begin(), ans.end());  // 將 ans 排序後印出
        int m = (int)ans.size();
        for(int i=0; i<m; i++) {
            printf("%d", ans[i]);
            if (i == m-1) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}

寫法2,用 vector 儲存待走訪的佇列,使用時間約為 0.1 s,記憶體約為 5 MB,通過測試。
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;

int main() {
    int dr[4] = {0, 1, 0, -1}, dc[4] = {1, 0, -1, 0};  // 四方位檢查用的位移量
    int H, W;  // 地圖 H*W
    while(scanf("%d %d", &H, &W) != EOF) {
        int grid[H][W];  // 讀取地圖
        for(int i=0; i<H; i++) {
            for(int j=0; j<W; j++) {
                scanf("%d", &grid[i][j]);
            }
        }
        int num = 1;  // 花地編號
        vector<int> ans;  // 答案
        for(int i=0; i<H; i++) {  // 依序掃過每一格
            for(int j=0; j<W; j++) {
                if (grid[i][j] == 1) {  // 如果此格是花地
                    int tot = 0, head = 0;  // 這區的圍籬數量,從 que 讀取資料的索引值
                    vector<pair<int, int>> que = {make_pair(i, j)};  // 待走訪的佇列
                    num++;  // 編號加 1
                    while(head < (int)que.size()) {  // 如果 head 還沒有出界繼續執行
                        int r = que[head].first, c = que[head].second;  // 從 que 讀取位置
                        head++;  // head 加 1
                        grid[r][c] = num;  // 設定 grid[r][c] 的編號
                        for(int k=0; k<4; k++) {  // 四方位檢查
                            int nr = r+dr[k], nc = c+dc[k];  // 要檢查的格子 (nr, nc)
                            if (nr >= 0 && nr < H && nc >= 0 && nc < W) {  // 如果 nr, nc 沒有出界
                                if (grid[nr][nc] == 1) {  // 如果是還沒有編號的花地
                                    grid[nr][nc] = num;  // 設定編號
                                    que.push_back(make_pair(nr, nc));  // (nr, nc) 加入 que
                                } else if (grid[nr][nc] == 0) {  // 如果是空地
                                    tot++;  // (r, c) 需要加一片圍籬
                                }
                            }
                        }
                    }
                    ans.push_back(tot);  // 將 tot 加入 ans
                }
            }
        }
        sort(ans.begin(), ans.end());  // 將 ans 排序後印出
        int m = (int)ans.size();
        for(int i=0; i<m; i++) {
            printf("%d", ans[i]);
            if (i == m-1) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}


沒有留言:

張貼留言