백준 3108.
의식의 흐름 알고리즘
문제 자체는 쉽다. 어떤 규칙인지도 찾기는 쉽다.
주어진 점들로 사각형들을 다 그렸을 때, 이어서 그릴 수 있을만큼 그린다음 안되는 경우만 연필을 들면 되는 문제다.
예제3의 경우는 총 5개의 사각형이 있는데 꼭짓점이 맞닿으면 안떼고 사각형을 그릴 수 있다. 갔던 길은 중복해서 그을 수도 있다고 했기 때문에 꼭짓점이 아닌 모서리가 겹쳐도 안떼고 사각형을 그릴 수 있다는 점을 주의하자. 그래서 파란색으로 그은 사각형은 안떼고 갈 수 있으며 핑크색 사각형은 덩그러니 혼자 있으므로 한번 떼서 이동해야 한다.
이것도 이를테면 DFS느낌으로 가면 될 것 같다. 사각형들을 그렸을 때 겹치는 부분이나 맞닿는 부분이 하나라도 있으면 한번에 갈 수 있다. 아닌가 BFS인가..? A사각형에서 출발한다고 가정하고, 그 사각형과 겹치는지 안겹치는지를 다 체크해야 하지 않을까..?
여기서 잘못 접근한 발상 첫번째. 접하게 되는 조건을 고려를 했는데 접하지 않는 조건을 고려를 한다.
이것도 머리가 좀 팽팽 돌았는데 비교군1의 사각형이 검은색이라 가정하고 좌표도 (x1, y1)(x2, y2)로 가정을 하자. 그러고서 맞닿지 않게되는 경우의 수인 파란색 사각형이 총 6가지인데 이 때 x1,x2 / y1,y2는 작은 수대로 준다는 전제하에서 푸나보다. 문제에서 그런 조건은 없긴했는디..
#사방에 있는 사각형(1~4)인 경우 맞닿지 않는다고 본다.
if x4 < x1 or x3 > x2 or y2 < y3 or y4 < y1 :
return False
#비교군 사각형 안에 폭 담긴다
if x1 < x3 and x2 > x4 and y4 < y2 and y3 > y1 :
return False
#비교군 사각형을 담는다
if x1 > x3 and x2 < x4 and y4 > y2 and y3 < y1 :
return False
솔직히 하나하나 다 적고나서야 안헷갈렸다ㅠ 아니 왤케 헷갈림..
아무튼 그다음에 고려할 것은 두번째. 서로 서로가 접하는 관계인 것을 어떻게 기록할 것인가. 유니온파인드라는 것을 사용한다고 한다. 그래프 알고리즘으로 두 노드 간에 연결관계(접한다)가 있다면 이어주는 그런 형태인 셈. 유니온파인드에 대해 알고싶다면
#유니온 파인드의 기본 규칙 - 인덱스와 같은 값의 value의 배열을 만듦
parent = [i for i in range(n)]
def find(i):
if i != parent[i]:
return find(parent[i])
else:
return i
def union(a, b):
a = find(a)
b = find(b)
if a < b :
parent[b] = a
parent[a] = b
유니온을 하여 부모노드를 설정하고, 최종적으로 같은 부모노드를 가지고 있는지 확인하는 과정까지.
이런 일련의 과정을 거쳐서 parent에는 [0,0,0,0,4] 가 남게되고 한붓그리기로 가능한게 총 두 조합이라는 것이 나오는데 시작점이 0,0인 경우는 처음 펜을 놓은 상태 그대로 출발할 수 있기에 총 수에서 -1을 해줘야 하는 것 까지 고려해주면 된다.
틀린답
def bf13():
n = int(input())
sqr = []
chk = False
for _ in range(n):
x1, y1, x2, y2 = map(int, input().split())
if x1==0 and y1 ==0 or x2==0 and y2==0 :
chk = True
sqr.append(((x1,y1), (x2,y2)))
#유니온 파인드의 기본 규칙 - 인덱스와 같은 값의 value의 배열을 만듦
parent = [i for i in range(n)]
def meet(a, b):
x1, y1, x2, y2 = a[0][0], a[0][1], a[1][0], a[1][1]
x3, y3, x4, y4 = b[0][0], b[0][1], b[1][0], b[1][1]
#사방에 있는 사각형(1~4)인 경우 맞닿지 않는다고 본다.
if x4 < x1 or x3 > x2 or y2 < y3 or y4 < y1 :
return False
#비교군 사각형 안에 폭 담긴다
if x1 < x3 and x2 > x4 and y4 < y2 and y3 > y1 :
return False
#비교군 사각형을 담는다
if x1 > x3 and x2 < x4 and y4 > y2 and y3 < y1 :
return False
return True
def find(i):
if i != parent[i]:
return find(parent[i])
else:
return i
def union(a, b):
a = find(a)
b = find(b)
if a < b :
parent[b] = a
else:
parent[a] = b
for i in range(n-1):
for j in range(i+1, n):
if meet(sqr[i], sqr[j]):
#몇 번 째 사각형끼리 union인지
union(i, j)
for i in range(n):
find(i)
#parent배열에서 동일한 숫자끼리 set으로 묶어 총 숫자 몇개의 조합인지 판별
res = len(set(parent))
if chk :
print(res -1)
else:
print(res)
최종 알고리즘
다른 풀이들을 봐도 영 모르겠어서 한참을 뒤지고 또 뒤지다가
x1, y1, x2, y2 = map(int, input().split())
if x1==0 or x2 ==0 :
if y1 <= 0 <= y2:
chk = True
if y1==0 or y2 == 0:
if x1<= 0 <= x2:
chk = True
if aa < bb :
parent[bb] = aa
else:
parent[aa] = bb
cnt = 0
for i in range(n):
if parent[i] == i:
cnt+=1
if chk :
print(cnt -1)
else:
print(cnt)
이 두부분을 고치고 나서야 해결이 되었다. 아니 사실 이렇게 풀거면 sqr에 set으로 저장할 필요도 없음.
뭔가 형식 상 그게 좋아보여서(?) set으로 했는데 필요 없는 작업이었고 set을 이용하려면 union에서 조금 다르게 처리를 해줘야 하는 듯 한데 이해가 안됨... 참고
set이 아닌 단순히 부모 노드를 본인 인덱스에 맞는 위치에 선언하는 방식
def bf13():
n = int(input())
sqr = []
chk = False
for _ in range(n):
x1, y1, x2, y2 = map(int, input().split())
if x1==0 or x2 ==0 :
if y1 <= 0 <= y2:
chk = True
if y1==0 or y2 == 0:
if x1<= 0 <= x2:
chk = True
sqr.append(((x1,y1), (x2,y2)))
#유니온 파인드의 기본 규칙 - 인덱스와 같은 값의 value의 배열을 만듦
parent = [i for i in range(n)]
def meet(a, b):
x1, y1, x2, y2 = a[0][0], a[0][1], a[1][0], a[1][1]
x3, y3, x4, y4 = b[0][0], b[0][1], b[1][0], b[1][1]
#사방에 있는 사각형(1~4)인 경우 맞닿지 않는다고 본다.
if x4 < x1 or x3 > x2 or y2 < y3 or y4 < y1 :
return False
#비교군 사각형 안에 폭 담긴다
if x1 < x3 and x2 > x4 and y4 < y2 and y3 > y1 :
return False
#비교군 사각형을 담는다
if x1 > x3 and x2 < x4 and y4 > y2 and y3 < y1 :
return False
return True
def find(i):
if i != parent[i]:
parent[i] = find(parent[i])
return parent[i]
def union(a, b):
aa = find(a)
bb = find(b)
if aa < bb :
parent[bb] = aa
else:
parent[aa] = bb
for i in range(n-1):
for j in range(i+1, n):
if meet(sqr[i], sqr[j]):
#몇 번 째 사각형끼리 union인지
union(i, j)
cnt = 0
for i in range(n):
if parent[i] == i:
cnt+=1
if chk :
print(cnt -1)
else:
print(cnt)
와 아니 근데 골드단계부터는 정말.. 너무..어렵다...............................................................
'IT이야기 > CODE PRACTICE' 카테고리의 다른 글
[완전탐색] 백준 1759 파이썬 풀이(백트래킹을 곁들인) (0) | 2024.11.05 |
---|---|
[그리디] 백준 2875 - 파이썬 (0) | 2024.10.28 |
[분할정복] 백준 1517 - 파이썬 (0) | 2024.10.27 |
[분할정복] 백준 2447 - 파이썬 (0) | 2024.10.26 |
[이분탐색] 백준 10816 - 파이썬 (0) | 2024.10.24 |