백준 15684 : 사다리 조작 (Python)

liliili·2023년 3월 3일

백준

목록 보기
192/214

본문 링크


import sys
input=sys.stdin.readline
from itertools import combinations

LMI=lambda:list(map(int,input().split()))
MI=lambda:map(int,input().split())
G=lambda x:[ LMI() for _ in range(x) ]
V=lambda x,y:[ [False]*y for _ in range(x) ]


def Ladder(P):
    Ladder_graph=[ i[:] for i in graph]

    for x,y in P:
        Ladder_graph[x][y]=y+2
        Ladder_graph[x][y+1]=y+1

    total = 0

    for i in range(N):
        x,y=[0,i]
        while True:
            if x==H:
                if y==i:
                    total+=1
                    break
                else:
                    return False
            if Ladder_graph[x][y]:
                y=Ladder_graph[x][y]-1
                x+=1
            else:
                x+=1
    if total==N:
        return True
    return False

N,M,H=MI()
graph=[ [0]*N for _ in range(H+1) ]
L=[] ; P=[]
for i in range(M):
    a,b=MI()
    graph[a-1][b]=b
    graph[a-1][b-1]=b+1

for i in range(H+1):
    for j in range(1,N):
        if [graph[i][j-1],graph[i][j]]==[0,0]:
            L.append((i,j-1))


if Ladder([]):
    print(0)
    exit(0)

for i in range(len(L)):
    if Ladder([L[i]]):
        print(1)
        exit(0)

for i in combinations(L, 2):
    if Ladder(i):
        print(2)
        exit(0)
for i in combinations(L,3):
    if Ladder(i):
        print(3)
        exit(0)

print(-1)

📌 어떻게 접근할 것인가?

오랜만에 푸는데 엄청 오래걸린 문제였습니다. 6시간정도 푼것같습니다.
이 문제를 풀면서 새로얻은 지식도 많은것같네요. 좋은문제인것같습니다.

문제는 간단합니다.
사다리에 다리를 3개까지 놓아서 NN 번째 사다리에서 출발해서 NN번지점에 도착하도록 만드는 것입니다.

일단 NN , MM , HH 라는 변수가 주어졌는데 처음에 입력이 이해가안됬습니다.

NN 은 가로길이 , HH 는 세로길이 , MM 은 초기에 놓아지는 사다리의 개수입니다.

graph=[ [0]*N for _ in range(H+1) ]

따라서 배열의 길이는 이렇게 설정해주고

MI=lambda:map(int,input().split())
for i in range(M):
    a,b=MI()
    graph[a-1][b]=b
    graph[a-1][b-1]=b+1

MM 번 동안 사다리를 미리 놓아주었습니다.

따라서 예제 3번을 입력했을때 그래프의 상태는 위와같습니다.
높이는 인덱스 관리가 편하게 하나 더 크게 잡아줬습니다.

그래프를 어떻게 구현할지에 대해 가장많은 시간을 썼던것같았습니다.

만약 가로의 길이를 NN 으로 잡았을때 , 같은 줄에 1과2 , 3,4의 사다리가있을때

사다리를 전부 1 로 설정해버리면 [1,1,1,1,0] 과 같이 됩니다. 따라서 2번줄에서 시작을 하면 왼쪽으로 가야할지 오른쪽으로 가야할지 구분이 안됩니다.

그렇다고 [1,1,0,0,1,1,0,0] 이렇게 배열의 길이를 2배로 늘려버리면 하나의 점에서 탐색하기 때문에 인덱스 2 인부분에서 왼쪽사다리를 탈지 오른쪽 사다리를탈지 명확하지 않습니다.

따라서 그래프 자체에 가로인덱스 값을 넣어서 위치를 잡아주었습니다.

다시 그림을 보면 첫번째 줄애 [2,1,0,0,0] 은 0번째인덱스는 2-1 번째 인덱스로 가야한다는 뜻이고 1번째인덱스는 1-1 번째인덱스로 가는걸로 설정해서

[2,1,4,3,0] - 이처럼 한줄에 사다리가 2개가 있을경우도 제대로 탐색가능하게됩니다.

for i in range(H+1):
    for j in range(1,N):
        if [graph[i][j-1],graph[i][j]]==[0,0]:
            L.append((i,j-1))

초기 MM 번의 입력을 받고 사다리를 설정한 다음에 사다리를 놓을 수 있는 지점의 인덱스를 모두 LL 리스트에 담았습니다.

for i in combinations(L, 2):
    if Ladder(i):
        print(2)
        exit(0)

이후 콤비네이션을 통해서 사다리를 놓을 수 있는 모든지점중 2개를 선택하는 경우를 구했습니다.

def Ladder(P):
    Ladder_graph=[ i[:] for i in graph]

    for x,y in P:
        Ladder_graph[x][y]=y+2
        Ladder_graph[x][y+1]=y+1

    total = 0

    for i in range(N):
        x,y=[0,i]
        while True:
            if x==H:
                if y==i:
                    total+=1
                    break
                else:
                    return False
            if Ladder_graph[x][y]:
                y=Ladder_graph[x][y]-1
                x+=1
            else:
                x+=1
    if total==N:
        return True
    return False

사다리 탐색 부분입니다.

Ladder_graph=[ i[:] for i in graph]

원래는 2차원 배열을 복사할때 deepcopy 를 사용했는데 , 슬라이싱과 컴프리헨션을 사용하는게 매우 빠릅니다. 이번에 처음알았는데 속도차이가 꽤 났습니다. 깊은복사를 사용하면 시간초과가 발생합니다.

for x, y in P:
    Ladder_graph[x][y] = y + 2
    Ladder_graph[x][y + 1] = y + 1

모든 경우의 수에 대해서 0~3 개의 사다리를 놓아주었습니다.

total = 0
    for i in range(N):
        x,y=[0,i]
        while True:
            if x==H:
                if y==i:
                    total+=1
                    break
                else:
                    return False
            if Ladder_graph[x][y]:
                y=Ladder_graph[x][y]-1
                x+=1
            else:
                x+=1
    if total==N:
        return True
    return False

시작은 제일 위에서 시작하고 사다리를 타고 가면서 만약 사다리 타기가 끝났을때 위치가 변동되었다면 바로 return False 를 해주었습니다. 만약 위치가 그대로였다면 total 변수에 1 을 추가하고 다음 위치에서 다시 사다리를 타고갑니다.

이후 total 변수가 NN 과 값이 같다면 i 번째 사다리에서 시작해서 모두 i 번째 도착점으로 도달한다는 뜻이므로 True 를 반환합니다.

if Ladder([]):
    print(0)
    exit(0)

for i in range(len(L)):
    if Ladder([L[i]]):
        print(1)
        exit(0)

for i in combinations(L, 2):
    if Ladder(i):
        print(2)
        exit(0)
for i in combinations(L,3):
    if Ladder(i):
        print(3)
        exit(0)
print(-1)

사다리는 최대 3개 까지 놓을 수 있기 때문에 1개놓았을때 , 2개놓았을때 , 3개놓았을때 모두 구했습니다.

처음에 그래프만 잘 잡아줬다면 금방풀었을텐데 그래프를 어떻게 구성할지에 시간을 많이 썼습니다.
또 모든 경우의 수도 처음엔 백트래킹을 사용했는데 파이썬은 재귀가 너무 느리니깐 가능하면 itertools 를 쓰는게 좋을거같네요.

0개의 댓글