코딩 문제

백준 2563- 색종이

토리쟁이 2023. 1. 7. 17:29

https://www.acmicpc.net/problem/2563

 

2563번: 색종이

가로, 세로의 크기가 각각 100인 정사각형 모양의 흰색 도화지가 있다. 이 도화지 위에 가로, 세로의 크기가 각각 10인 정사각형 모양의 검은색 색종이를 색종이의 변과 도화지의 변이 평행하도록

www.acmicpc.net

 

이 문제는 색종이를 입력받아 전체 색종이의 넓이를 구하면 되는 문제이다.

처음에는 전체 색종이의 넓이에서 겹치는 부분을 빼도록 로직을 구현했는데

테스트 예제에서는 답이 잘 나왔지만 아마 겹치는 부분이 많은 경우를 포함시키지 못 해 오답처리 되는 것 같다.

일단 오류 코드는 다음과 같다.

def check(xy1, xy2): # 겹치는지 아닌지 체크해주는 함수
    x = [i for i in range(xy1[0][0], xy1[0][0]+11)]  # x 범위
    y = [i for i in range(xy1[0][1], xy1[0][1] + 11)]  # y 범위

    for i in xy2:
        if i[0] in x and i[1] in y:
            return 1

    return 0

def diff(xy1, xy2):  # 겹치는 부분의 넓이를 구해주는 함수
    x_d1 = max(xy1[0][0], xy2[0][0])
    x_d2 = min(xy1[0][0]+10, xy2[0][0]+10)
    y_d1 = max(xy1[0][1], xy2[0][1])
    y_d2 = min(xy1[0][1]+10, xy2[0][1]+10)
    result = (x_d2-x_d1)*(y_d2-y_d1)
    return result


n = int(input())
sq = []
xy = [] # 한 색종이의 4개의 꼭짓점을 담아둘 리스트
for i in range(n):
    x, y = list(map(int, input().split()))
    xy.append([x, y])
    xy.append([x, y+10])
    xy.append([x+10, y])
    xy.append([x+10, y+10])
    sq.append(xy)
    xy = []  # .clear() 사용시 -> sq 안이 빈 리스트로 바뀌어버림

flag = 0 # 겹치는 경우:1 / 겹치지 않는 경우 0
ans = 100 * n  # 하나도 겹치지 않을 경우, 전체 색종이의 넓이
flags = {}  # 중복 이슈를 방지하기 위해 만들어둔 딕셔너리
result1 =0
result2 = 0
for i in range(len(sq)-1):
    for j in range(i+1, len(sq)):
        flag = check(sq[i], sq[j]) # 겹치는 부분이 있는지 확인
        if flag == 1:  # 겹치는 부분이 있는 경우
            if i in flags.keys() and j in flags.keys(): # 이미 전에 겹치는 부분을 계산한 색종이일 경우
                flags[j].append(i)
                for k in flags[j]:
                    
                result1 = diff(sq[i], sq[flags[i]])
                result2 = diff(sq[j], sq[flags[j]])
                result = abs(result1-result2)  # 두 넓이의 차
            if i not in flags.keys():  # 전에 겹치는 부분이 없던 색종이일 경우
                flags[j] = [i]  # 겹치는 부분이 있는 색종이로 판정하고 어떠한 색종이와 겹쳤는지 딕셔너리 형태로 저장
                result = diff(sq[i], sq[j])
            ans -= result

print(ans)

 

이미 겹쳐진 색종이로 계산을 한 색종이의 경우, 전에 이미 차감했으므로 단순히 뺄 것이 아니라, 차이나는 부분만 전체 넓이에서 빼줘야한다. 

하나의 색종이가 다른 색종이들과 여러 번 겹쳐질 경우, 어떠한 색종이와 겹쳐졌었는지 딕셔너리에 리스트 형태로 계속 추가시킨 후 해당 색종이의 values에 다 접근하여 넓이를 비교 후, 더 큰 넓이가 계산되었을 경우 그 차이만큼 빼주고 아니면 그냥 넘기면 될 것 같은데 생각만해도 굉장히 복잡하다는 것을 알 수 있다.

그래서 구글링을 시도해보았고, 이차원 배열을 통해 굉장히 쉽게 문제 해결이 가능해 그 로직을 적용하였다.

 

이 문제의 조건을 보면, 도화지의 크기가 제한되어 있고 그 크기도 크지 않기 때문에 이차원 배열로 도화지를 그리고 그 안에서 색종이 영역만 칠한 후 그 색종이 영역의 갯수만 세면 넓이가 나온다.

키포인트는 색종이가 붙여진 영역은 중복될 수 있고, 중복이  되더라도 하나만 카운트가 되게끔 하면 된다.

 

구현)

1. 100*100의 이차원 배열 생성 후 0으로 초기화(어떠한 색종이도 없는 상태)

2. 색종이를 입력받아 10*10 영역에 1로 채우기

3. 전체 배열에서 1의 갯수 세기 == 전체 넓이

 

n = int(input())
p = [[0]*100 for i in range(100)]
result = 0
for i in range(n):
    a, b = list(map(int, input().split()))
    for j in range(10):
        for k in range(10):
            p[a+j][b+k] = 1

for i in p:
    result += i.count(1)

print(result)

아래 1을 세는 로직에서 단순히 p.count(1)을 해버리면 0이 나온다.

p의 원소들은 모두 100 크기의 리스트들로 구성되어있지 1로 구성되어 있는 것이 아니기 때문이다.

따라서, 반복문을 통해 리스트 한 줄에 접근하여 1의 갯수를 세어주면 된다.

 

느낀점)

문제를 있는 그대로 구현하려는 습관을 버리고 조금 더 원리적으로 쉽게 구현할 수 있는 방법을 계속해서 생각하는 연습을 해야겠다.

단순히 문제 난이도가 높다고 해서 구현하기 까다로운 것이 아니며, 원리를 이해하고 조금 더 쉽게 구현이 가능하도록 하는 데 시간이 걸릴 뿐이라는 것을 명심하자.

 

'코딩 문제' 카테고리의 다른 글

백준 14889 - 스타트와 링크  (0) 2023.01.10
백준 14888 - 연산자 끼워넣기  (0) 2023.01.10
백준 3053- 택시 기하학  (0) 2023.01.06
백준 2057 - 팩토리얼 분해  (0) 2023.01.04
백준 17103 - 골드바흐 파티션  (0) 2023.01.03