クルトンのプログラミング教室

Pythonの使い方やPythonを使った競技プログラミングの解法などを解説しています。

Pythonで理解する蟻本「3-2 領域の個数」(p.150)

この記事は「プログラミングコンテストチャレンジブック第2版」(蟻本)の
「3-2 領域の個数」(p.150)
のコードをPythonで書き直したものとなっています。

入力

w\,h\,n\\x1_1\,…\,x1_n\\x2_1\,…\,x2_n\\y1_1\,…\,y1_n\\y2_1\,…\,y2_n

入力例



10 10 5
1 1 4 9 10
6 10 4 9 10
4 8 1 1 6
4 8 10 5 10


解答

from collections import deque

# 入力
W, H, N = map(int,input().split())
X1 = list(map(int,input().split()))
X2 = list(map(int,input().split()))
Y1 = list(map(int,input().split()))
Y2 = list(map(int,input().split()))

# 塗りつぶし用
fld = [[False] * (N * 6) for _ in range(N * 6)]

# x1, x2を座標圧縮し、座標圧縮した際の幅を返す
def compress(x1, x2, w):
    xs = []
    
    for i in range(N):
        for d in range(-1, 2):
            tx1 = x1[i] + d
            tx2 = x2[i] + d
            if 1 <= tx1 <= W:
                xs.append(tx1)
            if 1 <= tx2 <= W:
                xs.append(tx2)
    xs = list(set(xs))
    xs.sort()
    
    for i in range(N):
        x1[i] = xs.index(x1[i])
        x2[i] = xs.index(x2[i])
    
    return x1, x2, len(xs)

# 座標圧縮
X1, X2, W = compress(X1, X2, W)
Y1, Y2, H = compress(Y1, Y2, H)

# 腺のある部分を塗りつぶし
for i in range(N):
    for y in range(Y1[i], Y2[i] + 1):
        for x in range(X1[i], X2[i] + 1):
            fld[y][x] = True

# 領域を数える
ans = 0
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
for y in range(H):
    for x in range(W):
        if fld[y][x]:
            continue
        ans += 1
        
        # 幅優先探索
        que = deque([(x, y)])
        while len(que) != 0:
            sx, sy = que.popleft()
            
            for i in range(4):
                tx = sx + dx[i]
                ty = sy + dy[i]
                if tx < 0 or W <= tx or ty < 0 or H <= ty:
                    continue
                if fld[ty][tx]:
                    continue
                que.append((tx, ty))
                fld[ty][tx] = True

print(ans)