def check():
    global N,H
    for ind in range(N):
        current_ind = ind
        for k in range(H):
            if visited[k][current_ind]:
                current_ind += 1
            elif current_ind>0 and visited[k][current_ind-1]:
                current_ind -= 1
        if current_ind != ind:
            return False
            
    return True




def dfs(row_index,col_index,cnt,limit):
    global N,H,result
    if cnt > limit:
        return
    if check():
        result = min(cnt,result)
        return
    for k in range(row_index,H):
        if k == row_index:
            search_y = col_index
        else:
            search_y = 0
        for y in range(search_y,N-1):
            if not visited[k][y] and not visited[k][y+1]:
                visited[k][y] = 1
                dfs(k,y+2,cnt+1,limit)
                visited[k][y] = 0









N,M,H = map(int,input().split())
# H는 놓을 수 있는 개수
# M 은 가로선의 개수
# 앞은 가로선의 위치 뒤는 2개의 위치
result = float('inf')
visited = [[0]*N for _ in range(H)]
for _ in range(M):
    row,node = map(int,input().split())
    visited[row-1][node-1] = 1
if check():
    print(0)
else:
    for limit_insert_rows in range(1,4):
        dfs(0,0,0,limit_insert_rows)
        if result != float('inf'):
            print(result)
            break
    else:
        print(-1)

문제에서 최고로 놓을  수 있는 사다리의 개수는 3개가 최대이므로, 기본적으로 전체 사다리를 놓을 수 있는 위치를 골라서 재귀로 푸는 방식으로 해줬다. 그리고 result값이 바뀌면 break로 탈출을 해줬다. 

사다리의 가로선의 정보를 node-1,node 둘 중 왼쪽인 node-1에 저장해줬다.

그리고 사다리가 놓을 수 없는 조건은 현재 위치인 [가로선][세로선] 과 [가로선][세로선+1] 위치에 전부 0이 존재하면 사다리를 놓을 수 있다고 가정했다. 그리고 난뒤 재귀함수에서 y축을 +2만큼 이동시켜줬다. 그 이유는 해당 가로선에서  내가 고른 세로선에서 사다리를 놓게 되면 세로선 과 세로선+1의 위치에는 사다리를 놓을 수 없다. 그러므로, y를 +2 해주었다.

 

그리고, 가로선 중에서 주어진 row_index가 같을때에만 그러고 그 다음 가로선부터는 전체 세로선에 대해서 탐색하면 된다.

 

이렇게 사다리를 추가해주면서, check()라는 함수를 통해, 문제에 주여진 조건인 start_index와 사다리를 통과하고 나온 index가 같은지 비교를 해줘서 True,False 값을 반환시켜주었다.

 

이렇게 풀었지만, 다른 사람들의 코드를 공부하고 더 나은 풀이로 나중에 고쳤다.

import sys
def check():
    global N,H
    for ind in range(N):
        current_ind = ind
        for k in range(H):
            current_ind += visited[k][current_ind]
        if current_ind != ind:
            return False
            
    return True




def dfs(ladder_index,cnt,limit):
    global N,H,result
    if limit == cnt:
        if check():
            result = limit
        return
    for ind in range(ladder_index,N*H):
        row_index = ind//N
        col_index = ind%N
        if col_index == N-1:
            continue
        if not visited[row_index][col_index] and not visited[row_index][col_index+1]:
            visited[row_index][col_index] = 1
            visited[row_index][col_index+1] = -1
            dfs(ind+2,cnt+1,limit)
            if result != 4:
                return
            visited[row_index][col_index] = 0
            visited[row_index][col_index+1] = 0





N,M,H = map(int,input().split())
# H는 놓을 수 있는 개수
# M 은 가로선의 개수
# 앞은 가로선의 위치 뒤는 2개의 위치
if M == 0:
    print(0)
else:
    result = 4
    call = 0
    visited = [[0]*N for _ in range(H)]
    for _ in range(M):
        row,node = map(int,sys.stdin.readline().split())
        visited[row-1][node-1] = 1
        visited[row-1][node] = -1

    if check():
        print(0)
    else:
        for k in range(1,4):
            dfs(0,0,k)
            if result != 4:
                break
        if result != 4:
            print(result)
        else:
            print(-1)

 기본적인 원리는 비슷하다. 하지만 몇몇 부분에서 백트래킹을 해줬다.

먼저 좌측에만 가로선의 정보를 저장했던 거에서, 좌우측에 정보를 저장해줬다. 대신 그 값을 1과 -1을 저장시켜줬다.

이전 코드에서 좌측에 있는지 우측에 있는지 판별해서 세로선의 index를 바꿔주던거에서 좀 더 깔끔하게 바꿔주었다.

 

그리고 매 함수마다 check를 해주던 것을 cnt == limit에 도달했을때, check를 해주었다.

또한 결과값의 기본값이 바뀌었을때 break를 해주는 곳을 여러군데 추가해줬다.

가장 크게 바뀐 것은 2중 for문을 돌리던 것을 단일 for문으로 바꿔주었다.

다음과 같이 세로선 8과 가로선 4일때를 그려보면 다음과 같이 사다리를 그릴수 있을 것이다.

 

여기서 각각의 교차선을 구한다고 생각하면,

 

다음과 같이 세로선 8* 가로선 4 만큼의 교차선이 나올 수 있다. 그러므로 문제에 주어진 N*H 만큼의 교차선이 주어질 수 있다.

 

이 중에서 사다리를 놓을 수 있는 교차선은 빨간색 동그라미가 쳐진 교차선 뿐이다. N번 세로선인 8번 세로선에는 그 다음 세로선이 없기 때문에 사다리를 놓을 수 없다.

 

그 외의 나머지 부분 교차선에 사다리를 놓는다고 생각하고 재귀함수를 돌리면 된다. 위에서 설명 했듯이 한 곳에 사다리를 놓을시, 해당 교차선 위치에서 가로로 2칸만큼 더 가야지만 사다리를 놓을 수 있다. 

 

    for ind in range(ladder_index,N*H):
        row_index = ind//N
        col_index = ind%N
        if col_index == N-1:
            continue
        if not visited[row_index][col_index] and not visited[row_index][col_index+1]:
            visited[row_index][col_index] = 1
            visited[row_index][col_index+1] = -1
            dfs(ind+2,cnt+1,limit)
            if result != 4:
                return
            visited[row_index][col_index] = 0
            visited[row_index][col_index+1] = 0

함수에서 이부분이 교차선을 나타내고, 교차선의 가로선과 세로선을 찾아내서 세로선이 마지막 위치한 것이면 넘어가고, 

사다리를 놓을 수 있으면, 사다리를 놓은 뒤, 해당 index에서 +2를 해줘서 재귀함수를 해준다.

 

구현은 안했지만, 가능한 또 다른 방법은 사다리를 놓을 수 있는 교차선들을 전부 구해놓은 상태에서, 조합을 이용해 사다리를 1개 놓았을 때, 2개 놓았을 때, 3개 놓았을 때,를 구분하고, 고른 사다리가 문제가 없는지 판별해주고, 문제에 조여진 조건대로 시작 index와 도착 index를 같은지 판별하는 방법을 써도 될것 같다.

 

 

문제 자체는 어렵게 보이지 않았지만, 실제로 구현하는데에 어려운점이 많았고, 많이 틀렸던 문제였다.

중간중간 가능성이 없는것을 멈춰주고, 시간을 줄이는데 노력해야지 풀 수 있었던 힘들었던 문제였다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 1068 트리  (0) 2021.01.20
[BOJ/백준] 9019 DSLR  (0) 2021.01.20
[BOJ/백준] 18231 파괴된 도시  (0) 2021.01.18
[BOJ/백준] 9252 LCS 2  (0) 2021.01.17
[BOJ/백준] 2565 전깃줄  (0) 2021.01.17
# 18231 파괴된 도시






N,M = map(int,input().split())

graph = {i:[] for i in range(N+1)}

for _ in range(M):
    node1,node2 = map(int,input().split())
    graph[node1].append(node2)
    graph[node2].append(node1)

destory_cnt = int(input())
destroy_citys = list(map(int,input().split()))
result = []
destroyed = []
for destroy_city in destroy_citys:
    temp = [destroy_city]
    for city in graph[destroy_city]:
        if city not in destroy_citys:
            break
        else:
            temp.append(city)
    else:
        result.append(destroy_city)
        destroyed.extend(temp)
    if len(set(destroyed)) == destory_cnt:
        print(len(result))
        result.sort()
        print(' '.join(list(map(str,result))))
        break
else:
    print(-1)

이 문제는 여러가지 경우의 답이 나올수 있으나, 정답이 되는 1개만 출력하면 된다.

 

기본 원리는 이렇다. 그래프라는 딕셔너리에 양방향으로 노드를 전부 저장해놓는다.

 

그리고 파괴된 도시 목록을 입력을 받아, for문을 돌리는데, 만약 파괴된 도시 중 하나의 인접 노드들 중에 파괴된 노드들이 하나도 없으면 for문을 중지하고, 나온다. 그렇지 않을경우는 해당 도시는 파괴가 시작된 도시로 볼 수 있으므로, 결과목록에 넣어준다.

또한 해당 도시로 폭파된 도시 목록도 별도의 리스트에 저장해준다.

 

이렇게 반복을 해주면서, 별도로 저장해놓은 폭파된 도시목록을 set을 해줬을때의 길이가, 전체 파괴된 도시수와 동일하다면 for문을 중지하고, 결과를 보여준다.

 

위의 해당사항이 하나도 없을 시에는 -1을 출력해주면 된다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 9019 DSLR  (0) 2021.01.20
[BOJ/백준] 15684 사다리 조작  (0) 2021.01.20
[BOJ/백준] 9252 LCS 2  (0) 2021.01.17
[BOJ/백준] 2565 전깃줄  (0) 2021.01.17
[BOJ/백준] 1038 감소하는 수  (0) 2021.01.16
# Longest Common Subsequence
#  최장 공통 부분 수열
def LCSlength(X,Y):
    for i in range(1,len(X)+1):
        for j in range(1,len(Y)+1):
            if X[i-1] == Y[j-1]:
                LCS_ARRAY[i][j] = LCS_ARRAY[i-1][j-1] + 1
            else:
                LCS_ARRAY[i][j] = max(LCS_ARRAY[i][j-1],LCS_ARRAY[i-1][j])
    
def findBackTrack(C,X,Y,i,j):
    if i == 0 or j == 0:
        return ''
    if X[i-1] == Y[j-1]:
        return findBackTrack(C,X,Y,i-1,j-1) + X[i-1]
    if C[i][j-1] > C[i-1][j]:
        return findBackTrack(C,X,Y,i,j-1)
    return findBackTrack(C,X,Y,i-1,j)




string_1 = list(input())
string_2 = list(input())
LCS_ARRAY = [[0]*(len(string_2)+1) for _ in range(len(string_1)+1)]
LCSlength(string_1,string_2)
result = findBackTrack(LCS_ARRAY,string_1,string_2,len(string_1),len(string_2))
print(len(result))
print(result)

이론에 대한 설명은 다른 사람들의 블로그에 더 자세히 설명되어있고,

 

저같은 경우 ko.wikipedia.org/wiki/%EC%B5%9C%EC%9E%A5_%EA%B3%B5%ED%86%B5_%EB%B6%80%EB%B6%84_%EC%88%98%EC%97%B4

 

최장 공통 부분 수열 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 최장 공통 부분수열 문제는 LCS라고도 불린다. 이는 주어진 여러 개의 수열 모두의 부분수열이 되는 수열들 중에 가장 긴 것을 찾는 문제다.(종종 단 두 개중 하

ko.wikipedia.org

위키를 참조하면서 풀었다.

 

LIS와 LCS같은 DP 문제들은 반복 숙달이 되지 않으면 실전에서 푸는게 어려워 보인다.

 

 

제가 이해한 기본적인 원리는 다음과 같다.  문자열 S1 과 문자열 S2가 있으면, 이 두 문자열의 길이만큼의 2차원 행렬을 만들어준다.

 

이 행렬은 두 문자열이 같은 문자열을 얼마나 가지고 있는지 판별이 되는 행렬이다. 이행렬의 이름을 LCS라 하고, 이 행렬의 크기는 (문자열 S1의 길이 +1) X (문자열 S2의 길이 +1)이 된다. 왜냐하면 아무것도 선택하지 않았을때를 초기화 해주는 부분이 있기 때문이다.

 

그리고 S1,S2를 기준으로 2중 for문을 돌려준다. 그러면서 s1과 s2에서 똑같은 문자열이 발견이 되면, 

LCS[i][j]에 = LCS[i-1][j-1] +1 을 해준다.

왜 대각선 성분에 있는걸 더해주는 이유는 똑같은 문자열이 발견되기 직전의 각각의 index에 존재하는 값이 해당 부분직전까지의 최대값이기 때문이다.

코드를 보면 좀 헷갈리는 이유는 S1,S2의 index는 0부터 시작하기 때문이다. s1,s2의 i,j index의 LCS의 길이값은 LCS의 i+1,j+1 index에 저장이 된다.

만약 같지 않을 경우, [i-1, j], [i,j-1] 둘중 큰 값을 넣어주면 된다.

 

 

이렇게 구한 LCS를 이용해서 역추적으로 최장 공통 부분수열을 구성 값을 구해준다.

이 방법은 단 1개를 구하는 것이므로, 다른 방법은 위의 위키를 참조하거나, 다른 사람 블로그를 참조하길 바란다.

 

백트래킹으로 찾는 방법인데, 각가의 끝 인덱스에서 시작해서 만약에 두 인덱스에서의 S1,S2의 값이 같으면, 그 부분을 결과에 return 시켜주고, 현재 index는 두 값이 같기 때문에, i-1,j-1위치에서 백트래킹을 해준다.

그리고 그 외에는 LCS의 길이가 변경되는 순간이 같은 문자열이 존재하는 구간이기 때문에, 

[i-1][j] 와 [j][j-1의 LCS의 길이를 비교해서 더 큰 곳으로 이동해서 계쏙 반복을 해준다.

 

 

이 이론에 대해서는 아직 제대로 공부를 안했기 때문에 미숙한 부분이 많고,

 

저보다 더 나은 설명을 한 블로그들이 많으니 그 블로그를 참조하는 것을 권장합니다.

 

 

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 15684 사다리 조작  (0) 2021.01.20
[BOJ/백준] 18231 파괴된 도시  (0) 2021.01.18
[BOJ/백준] 2565 전깃줄  (0) 2021.01.17
[BOJ/백준] 1038 감소하는 수  (0) 2021.01.16
[BOJ/백준] 11052 카드 구매하기  (0) 2021.01.16
# 2565 전깃줄

N = int(input())
dp = [0]*501
arr = []
for _ in range(N):
    n1,n2 = map(int,input().split())
    arr.append((n1,n2))
arr.sort()
result = []
result.append(arr[0][1])
temp = 0
for k1,k2 in arr[1:]:
    for ind,val in enumerate(result):
        if val > k2 and k2 not in result:
            result[ind] =k2
            temp = 0
        elif val < k2 and k2 not in result:
            temp = k2
    if temp:
        result.append(temp)
    temp = 0


print(N-len(result))

개인적으로 어려워 하는 LIS 계열의 문제였다.

전깃줄 A를 기준으로 sort를 해준 다음에,  전깃줄 B를 기준으로 가장 긴 증가하는 수열의 구해주면 된다.

 

그렇게 되면, 선이 꼬이지 않고 설치할 수 있는 최대수가 되고, 주어진 N에서 최대수를 빼주면 된다.

 

코드 자체를 설명해보면 초기값으로 가장 첫번쨰  B 전깃줄을 넣어주고,

 

전체 데이터 들어진 행렬을 반복문을 돌리면서, LIS가 저장된 값과 비교를 하면서 LIS에 저장된 값보다 적으면 바로 그자리를  교체 해준다. 그리고 LIS가 저장된 값보다 전부다 크면 끝에 추가해주면 된다.

 

이 방법은 실제 LIS의 수열을 구하는 것이 아닌, 최장 길이를 구하기 위한 O(Nlogn)의 방식이다. 만약 실제 LIS를 구해야 하는 경우에는 다른 풀이를 찾아야한다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 18231 파괴된 도시  (0) 2021.01.18
[BOJ/백준] 9252 LCS 2  (0) 2021.01.17
[BOJ/백준] 1038 감소하는 수  (0) 2021.01.16
[BOJ/백준] 11052 카드 구매하기  (0) 2021.01.16
[BOJ/백준] 17086 아기 상어 2  (0) 2021.01.15
# 1038 감소하는 수
# X의 자릿수가 가장 큰 자릿수
import collections
def dfs(N):
    deque = collections.deque()
    for i in range(1,10):
        deque.append((i,str(i)))
    while deque:
        if len(result) == N+1:
            break
        cur_num,to_num = deque.popleft()
        if cur_num != 0:
            for k in range(cur_num):
                temp = to_num + str(k)
                deque.append((k,temp))
                result.append((temp))


N = 1023
result = []
for i in range(10):
    result.append(i)

if N >=10:
    dfs(N)
if len(result) > N:
    print(result[N])
else:
    print(-1)

 기본적인 백트래킹 문제이다. 처음에 1~9의 수를 넣어주고, 그보다 낮은 자리수에서 더 낮은 값을 넣어주면서 결과값에 넣어주고, 해당 길이를 초과하는 N이 나올시에는, -1을  출력하도록 했따.  

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 9252 LCS 2  (0) 2021.01.17
[BOJ/백준] 2565 전깃줄  (0) 2021.01.17
[BOJ/백준] 11052 카드 구매하기  (0) 2021.01.16
[BOJ/백준] 17086 아기 상어 2  (0) 2021.01.15
[BOJ/백준] 2564 경비원  (0) 2021.01.15
n=int(input())
card=[0]
card+=list(map(int,input().split()))
dp=[0]*(n+1)

dp[1]=card[1]
dp[2]=max((card[2],card[1]*2))

for i in range(3,n+1):
    dp[i]=card[i]
    for j in range(1,i//2+1):
        dp[i]=max(dp[i],dp[j]+dp[i-j])


print(dp[n])

초창기에 풀었던 문제이다. 

 

현재 DP에서의 초기값을 넣어주고, 두개를 더해서 i가 되는 경우를 갱신해주면 된다.

 

J의 range(i//2+1)인 이유는 중간까지 하면, 그 다음부터는 앞의것의 반복이기 때문이다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 2565 전깃줄  (0) 2021.01.17
[BOJ/백준] 1038 감소하는 수  (0) 2021.01.16
[BOJ/백준] 17086 아기 상어 2  (0) 2021.01.15
[BOJ/백준] 2564 경비원  (0) 2021.01.15
[BOJ] 5569 출근 경로  (0) 2021.01.15

 

#  17086 아기 상어
import collections
dx = [-1,0,1,-1,1,-1,0,1]
dy = [-1,-1,-1,0,0,1,1,1]

def bfs(x,y):
    deque = collections.deque()
    deque.append((x,y,0))
    visited = [[True]*M for _ in range(N)]
    visited[x][y] = True
    while deque:
        x,y,cnt = deque.popleft()
        if arr[x][y]:
            return cnt
        for i in range(8):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0<= nx <N and 0<=ny <M:
                if visited[nx][ny]:
                    deque.append((nx,ny,cnt+1))
                    visited[nx][ny] = False


N,M = map(int,input().split())


arr = [list(map(int,input().split())) for _ in range(N)]
result = 0
for x in range(N):
    for y in range(M):
        if not arr[x][y]:
            temp = bfs(x,y)
            result = max(temp,result)

print(result)

처음에는 각 점에서 BFS를 해서 상어와의 위치값이 가장 먼 값을 구하는 방법을 했다.

 

import collections
dx = [-1,0,1,-1,1,-1,0,1]
dy = [-1,-1,-1,0,0,1,1,1]
N,M = map(int,input().split())

arr = [list(map(int,input().split())) for _ in range(N)]
shark = collections.deque()
for i in range(N):
    for j in range(M):
        if arr[i][j] == 1:
            shark.append((i,j,arr[i][j]))

while shark:
    x,y,dis = shark.popleft()
    for i in range(8):
        nx = x + dx[i]
        ny = y + dy[i]
        if 0<= nx <N and 0<=ny<M:
            if not arr[nx][ny]:
                arr[nx][ny] = 1
                shark.append((nx,ny,dis+1))


print(dis-1)

 두번째 풀이는 그 반대로, 상어를 기준으로 bfs를 진행했다. 어차피 bfs는 거리순으로 진행되기 때문에, 가장 마지막에 나오는 dis가 최대 거리이므로, 마지막 dis에서 1을 빼줬다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 1038 감소하는 수  (0) 2021.01.16
[BOJ/백준] 11052 카드 구매하기  (0) 2021.01.16
[BOJ/백준] 2564 경비원  (0) 2021.01.15
[BOJ] 5569 출근 경로  (0) 2021.01.15
[BOJ] 2140 지뢰찾기  (0) 2021.01.15
M,N = map(int,input().split())
S = int(input())
# 북쪽 1 ==>0
# 남쪽 2 ==>N-1
# 서쪽 3 =>0
# 동쪽 4 => N-1
input_list = []
total_length = (N+M)*2
for tc in range(S+1):
    n1,n2 = map(int,input().split())
    if n1 == 1:
        input_list.append(n2)
    elif n1 == 2:
        input_list.append(M+N+(M-n2))
    elif n1 == 3:
        input_list.append(2*M+N+(N-n2))
    else:
        input_list.append(M+n2)

patrol = input_list[-1]
result = 0


for idx in range(S):
    temp = abs(input_list[idx]-patrol)
    result += min(temp,total_length-temp)
print(result) 

 

이 문제를 처음 풀때는 여러가지 경우로 나눌까 하다가, 생각해보니 이 문제는 직사각형의 테두리만 도는 것이고, 테두리의 둘레 값은 고정적이다. 그래서 한쪽만 구하면 반대편 값은 저절로 구해지는 것을 알수 있었다.

 

그래서 사각형을 하나의 직선 좌표로 바꾸었다.

순서는 북->동->남->서 순으로 (0,0) 좌표를 끊었다고 생각했을때 쭉 눌어지는 것처럼 했다.

 

위와 같은 직사각형을 밑과 같은 일직선으로 변경 후 두 점사이의 거리를 구했다.

전체 둘레에서 두 점사이의 거리를 뺀값과 두점 사이의 거리 중 더 짧은 것을 결과값에 더해주었다.

'알고리즘 > 백준' 카테고리의 다른 글

[BOJ/백준] 11052 카드 구매하기  (0) 2021.01.16
[BOJ/백준] 17086 아기 상어 2  (0) 2021.01.15
[BOJ] 5569 출근 경로  (0) 2021.01.15
[BOJ] 2140 지뢰찾기  (0) 2021.01.15
[BOJ] 1058 친구  (0) 2021.01.14

+ Recent posts