꾸준하게 거북이처럼
백준 사다리조작 파이썬 본문
15684번: 사다리 조작
사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선
www.acmicpc.net
1. 조건
- 최대 3개까지 가로선 추가, 3개를 넘으면 -1 출력
- 두 가로선이 접하면 안된다.
- 모두 i -> i 번 도착해야한다.
2. 구하고자 하는 것
- 모든 1~n번 세로선이 사다리를 타고 i->i 으로 도착할 수 있도록, 추가해야하는 가로선 개수 최솟값.
3. 가로선 선택 부분 풀이 및 설계
완전 탐색으로, 10*30 => 가로선이 접하면 안된다는 조건 하에서, 약 300개 중에서 최대 3개를 고르는 조합이라고 볼 수 있다.
300C3 * 300(체크하는 함수) => 기본적으로 1억이 넘는다.
중간에 서로 선이 접하면 안되는 조건도 있지만 그래도! 최적화를 좀 해야함.
ex) 최적화는, 최대 3개이므로 이 그 이상의 깊이는 조사하지 않는다던지.
이 문제는 2차원 배열의 지점을 선택하기에, for중복문을 사용해서 선택할 가로선을 정할 수 있는데,
for i in range(1,h+1):
for j in range(1,n+1):
if # 조건문:
line[i][j]=1
dfs(total+1,,,,)
line[i][j] = 0
보통 위의 형태로 코드를 쓸 수 있다. 하지만 최적화를 위해 일반 for중복문에서 좀 더 생각해보자.
backtracking을 한다면 아마, 끝까지 depth까지 들어가면 열 선택이 끝나서 이전 함수 호출로 돌아갈 것이다. 그러면?
다음 행 부터 선택할 수 있게 되고 이때는 당연히 1번 열부터 탐색을 해야한다. 하지만, 계속 쭉 재귀호출로 depth가 커지고 있는 상태라면?
굳이 1부터 탐색할 것이 아니라, 이어서 탐색해야하지 않겠는가, 지금 행에서 1번 열은 이미 전부터 탐색했는데?!
또한 두 가로선이 접하고 이어지면 안되기 때문에 선택한 부분으로부터 다 다음 부터 선택 가능하다.
이를 반영하면 대략 아래와 같은 코드를 생각할 수 있다.
def dfs(total, x,y):
# ,,,,,,
# ,,,,,,
for i in range(1,h+1):
if x == i:
col = y # 아직 같은 행 탐색 중이면 y부터
else:
col = 1 # 다른 행 탐색 중이면 1부터 다시.
for j in range(col,n+1):
if # 조건문:
line[i][j]=1
dfs(total+1,i,j+2)
line[i][j] = 0
조합 문제로, 라이브러리로 풀 수도 있지만, 삼성문제에서는 라이브러리 사용 금지.
**조합은 DFS로 풀 때, 보통 함수의 매개변수로 다음부터 선택할 수 있는 범위를 전달하는 방식으로 코드를 만들더라.
이 풀이는 pypy로 통과함.
코드
answer = 4 # 최대 3개까지 설치 가능하기 때문에 큰 값 4를 할당.
n,m,h = map(int,input().split())# 열, 가로선 개수, 행 입력.
line = [[0]* (n+1) for _ in range(h+1)]# 1번 ~ n번 열, 1~h번 행.
for _ in range(m):
a,b = map(int,input().split())
line[a][b]=1 # a번행의 b열와 b+1열이 연결 되있음.
def check():# 모든 1~n번이 i -> i 로 도착하는지 체크.
for x in range(1,n+1):
now = x # 현재 i번 시작점.
for r in range(1,h+1): # row 한칸씩 내려감.
if now > 1 and line[r][now-1]:# 만약 왼쪽에 사다리가 있다면 이동.
now -= 1
elif line[r][now]:# 만약 오른쪽에 사다리가 있다면 이동
now += 1
if x == now:# 이동후에 i -> i 번이라면, 계속 진행
continue
else: # 하나라도 아니라면 False 반환.
return False
return True # 모두 i-> i번도착 True .
def dfs(total,x,y):# 추가할 사다리 개수 세기
global answer,line
if check(): # 모두 i -> i번 도착인지 체크.
answer = min(answer,total) # answer update.
return
if total >= 3 or total>=answer: # 만약 현재까지 세는 개수가 3 이상이라면 그만두기, 또는 이미 answer 이상으로 크다면 그만.
return
for i in range(x,h+1):# 행
if x == i:
col = y # 아직 같은 행 검사 중이라면, 매개변수 이어서 y할당
else:
col = 1 # 백트래킹으로 다음 행 검사를 한다면, 열은 1부터 다시 탐색.
for j in range(col,n):# 가로선이 이어지면 안되므로, 체크.
if not line[i][j] and not line[i][j-1] and not line[i][j+1]:
line[i][j] =1 # 선택.
dfs(total+1,i,j+2) # 다 다음 열부터 탐색 가능.
line[i][j] = 0 # 백트래킹으로 다시 0 할당.
dfs(0,1,1) # (1,1)부터 선택 시작! => 전체 가능한 것 중에서 최대 3개를 뽑는 것이므로 조합. 조합은
# 매개변수에 인덱스를 넣으면서 범위를 좁혀간다.
if answer > 3: # 3보다 크면 -1
print(-1)
else:
print(answer)
완탐은 조합, 순열 자주 나옴! 💪
'Algorithm 문제 & 공부 > 완전탐색' 카테고리의 다른 글
백준 애너그램 파이썬 (0) | 2023.04.30 |
---|---|
프로그래머스 광물캐기 python (0) | 2023.04.20 |
백준 15661번 링크와 스타트 (0) | 2022.10.03 |
백준 14889번 파이썬 - 스타트와 링크 (1) | 2022.09.30 |
백준 14501 파이썬 - 퇴사 (1) | 2022.09.30 |