감사쟁이야
감사쟁이의 성장기록
감사쟁이야
  • 분류 전체보기 (130)
    • Java-Spring (0)
    • ComputerScience (0)
    • Project (64)
      • TIL, WIL (57)
      • Project Retrospect (7)
    • Problem Solving (63)
    • Book Review (1)
    • Culture & Discovery (0)
    • Daily Log (2)

블로그 메뉴

  • 홈
  • 깃허브
  • 방명록
hELLO · Designed By 정상우.
감사쟁이야

감사쟁이의 성장기록

[백준] 15684 : 사다리 조작 (삼성 SW 역량 테스트) / python
Problem Solving

[백준] 15684 : 사다리 조작 (삼성 SW 역량 테스트) / python

2022. 7. 5. 17:28

1. 문제 설명

 

15684번: 사다리 조작

사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선

www.acmicpc.net

 

입력

N → 세로선의 갯수 (2 ≤ N ≤ 10)

M → 가로선의 갯수 (0 ≤ M ≤ (N-1)×H)

H → 세로선마다 가로선을 놓을 수 있는 위치의 갯수 (1 ≤ H ≤ 30)

lines → 가로선의 정보 → (a, b) X M개 (1 ≤ a ≤ H, 1 ≤ b ≤ N-1) b번 세로선과 b+1번 세로선을 a번 점선 위치에서 연결

 

제한조건

두 가로선이 연속하거나 접하면 안된다.

 

구하고자 하는 바

사다리에 가로선을 추가해서 i번 세로선의 결과가 i번이 나올 수 있도록 추가해야하는 가로선 개수의 최솟값

단, 추가해야할 가로선이 3을 넘어가거나, 불가능한 경우는 -1을 출력한다.

 

2. 풀이 전 계획과 생각

행위의 규칙성, 패턴 파악

가로선을 추가하는 모든 경우를 구해주고

그에 따른 세로선의 결과가 i번이 나오는지 확인하자.

가로선을 추가하는 모든 경우를 구한다면,

  • H * (N - 1) = 10 * 30 → 2 ^ 300 ⏰
  • 제한조건이 있다. 추가해야할 가로선이 3을 초과하면 종료한다는 가지치기를 해주자.

 

필요한 로직

  • 가로선을 추가하는 모든 경우를 구하는 함수
    • 제한 조건 : 두 가로선이 연속하거나 서로 접하면 안된다.
    • 종료 조건 : 300개를 다 봤다면, 종료
    • 가지치기 조건 : 추가해야할 가로선이 3을 초과하면 종료한다.
  • 세로선의 결과를 알려주는 함수
  • 각 세로선의 결과 모두가 자기자신인지 확인해주는 함수

 

자세히

  • 가로선을 추가하는 모든 경우를 구하는 함수
    • 제한 조건 : 두 가로선이 연속하거나 서로 접하면 안된다.
    • 종료 조건 : 300개를 다 봤다면, 종료
    • 가지치기 조건 : 추가해야할 가로선이 3을 초과하면 종료한다.
    1. 둘 수 있다면 두는 경우와 놔두고 원래대로 복구
    2. 그외는 안두는 경우로 가기
  • 세로선의 결과를 알려주는 함수
    • 세로선을 가지고, i ↔ i +1 변경하기
  • 각 세로선의 결과 모두가 자기자신인지 확인해주는 함수
  • 가로선을 놔둘 수 있는지 체크하는 함수
  • 두 가로선이 연속하는지 체크하는 함수

 

3. 풀이

MAX_INT = 4

N, M, H = tuple(map(int, input().split()))
pre_lines = [tuple(map(int, input().split())) for _ in range(M)]
total_lines = [[0] * (N + 1) for i in range(H + 1)]
for line in pre_lines:
    a, b = line
    total_lines[a][b] = 1

def idx_to_coordinate(current_idx):
    y, x = current_idx // (N - 1) + 1, current_idx % (N - 1) + 1
    return y, x

def is_all_itself():
    expected_result = [num for num in range(0, N)]
    result = [num for num in range(0, N)]
    for y in range(1, H + 1):
        for x in range(1, N + 1):
            if total_lines[y][x] == 1:
                result[x - 1], result[x] = result[x], result[x - 1]

    return result == expected_result

def is_continuous(y, x):
    return total_lines[y][x + 1] == 1 and total_lines[y][x - 1] == 1

def can_put(current_idx):
    y, x = idx_to_coordinate(current_idx)
    return total_lines[y][x] != 1 and not is_continuous(y, x)

def combination(current_idx, count):
    global answer, total_lines
    if count >= MAX_INT or count > M:
        return

    if is_all_itself():
        answer = min(answer, count)
        return
    
    if current_idx == (N - 1) * H:    
        return

    y, x = idx_to_coordinate(current_idx)

    combination(current_idx + 1, count)

    if can_put(current_idx):
        total_lines[y][x] = 1
        combination(current_idx + 1, count + 1)
        total_lines[y][x] = 0

answer = MAX_INT
combination(0, 0)

if answer == MAX_INT:
    print(-1)
else:
    print(answer)

 

문제점

시간 초과 발생

 

4. 풀이하면서 막혔던 점과 고민

🤔 여기서 어떻게 더 시간을 줄여줄 수 있을까?

이미 갱신된 값보다 크면 확인해줄 필요가 없기에 가지치기 조건을 추가해주자.

if count >= answer or count > M:
	return

→ 시간 초과

더 줄여보자. 연속된 가로선이 나올 수 없는 곳에만 검사하자.

def combination(current_idx, count):
    global answer, total_lines
    if count >= answer or count > M:
        return

    if is_all_itself():
        answer = min(answer, count)
        return

    for candidate_idx in range(current_idx, len(candidate)):
        y, x = candidate[candidate_idx]
        if not is_continuous(y, x):
            total_lines[y][x] = 1
            combination(candidate_idx + 1, count + 1)
            total_lines[y][x] = 0

candidate = []
for i in range(1, H + 1):
    for j in range(1, N):
        if not is_continuous(i, j) and not total_lines[i][j]:
            candidate.append((i, j))

 

5. 2차 풀이

MAX_INT = 4
N, M, H = tuple(map(int, input().split()))
pre_lines = [tuple(map(int, input().split())) for _ in range(M)]
total_lines = [[0] * (N + 1) for _ in range(H + 1)]
for line in pre_lines:
    a, b = line
    total_lines[a][b] = 1

def is_all_itself():
    for x in range(1, N + 1):
        current_x = x
        for y in range(1, H + 1):
            if total_lines[y][current_x - 1] == 1:
                current_x -= 1
            elif total_lines[y][current_x] == 1:
                current_x += 1

        if current_x != x:
            return False

    return True

def is_continuous(y, x):
    return total_lines[y][x + 1] == 1 or total_lines[y][x - 1] == 1

def combination(current_idx, count):
    global answer, total_lines
    if count >= answer or count > M:
        return

    if is_all_itself():
        answer = min(answer, count)
        return

    for candidate_idx in range(current_idx, len(candidate)):
        y, x = candidate[candidate_idx]
        if not is_continuous(y, x):
            total_lines[y][x] = 1
            combination(candidate_idx + 1, count + 1)
            total_lines[y][x] = 0

candidate = []
for i in range(1, H + 1):
    for j in range(1, N):
        if not is_continuous(i, j) and not total_lines[i][j]:
            candidate.append((i, j))

answer = MAX_INT
combination(0, 0)

if answer == MAX_INT:
    print(-1)
else:
    print(answer)

 

6. 풀이 후 알게된 개념과 소감

  • 모든 경우를 따질 때, 시간복잡도를 낮추기 위해서는 가지치기할 것이 무엇이 있는지 확인할 수도 있지만,
    애초에 탐색할 때 필요한 부분만 탐색하는 방법도 있다.
    • 위의 문제에서 필요한 부분만 탐색하는 경우는 연속하지 않는 곳의 후보에만 탐색하는 것이다.

'Problem Solving' 카테고리의 다른 글

[백준] 2616 : 소형기관차 / python  (0) 2022.07.18
[프로그래머스] 양궁 대회 (2022 KAKAO BLIND) / python  (0) 2022.07.16
[백준] 16929 : Two Dots / python  (0) 2022.06.29
[백준] : 16637 괄호 추가하기 / python  (0) 2022.06.24
[프로그래머스] 메뉴 리뉴얼 (2021 KAKAO BLIND) / python  (0) 2022.06.03
    'Problem Solving' 카테고리의 다른 글
    • [백준] 2616 : 소형기관차 / python
    • [프로그래머스] 양궁 대회 (2022 KAKAO BLIND) / python
    • [백준] 16929 : Two Dots / python
    • [백준] : 16637 괄호 추가하기 / python
    감사쟁이야
    감사쟁이야
    sunzero0116@gmail.com

    티스토리툴바