본 풀이는 유튜브 강의를 토대로 작성했습니다.

 

2239번: 스도쿠

스도쿠는 매우 간단한 숫자 퍼즐이다. 9×9 크기의 보드가 있을 때, 각 행과 각 열, 그리고 9개의 3×3 크기의 보드에 1부터 9까지의 숫자가 중복 없이 나타나도록 보드를 채우면 된다. 예를 들어 다

www.acmicpc.net


문제 분석

- 스도쿠를 푸는 알고리즘을 작성해야 한다.

- 스도쿠의 각 칸에는 가로/세로/ 3 x 3 박스 내에 1~9까지 곂치는 수가 존재하지 않아야 한다.

 

본 문제를 풀이할 때 주의사항이 있다.

"가로/세로/ 3 x 3 박스 내에 1~9까지 곂치는 수"를 고려하여 문제를 풀었을 때 답이 나오지 않는 경우가 존재한다

그럴 때는 이전 칸으로 돌아가서 조건에 맞는 다른 수로 수정해야 한다.

 

따라서 이 문제는 백 트래킹을 사용해야 한다.


알고리즘

1. 스도쿠에 빈 공간(0)이 있는지 확인한다. 

2. 빈 공간이 없으면 스도쿠가 완성됐으므로 종료한다.

3. 빈 공간이 있으면 빈 공간(col, row)이 어딘지 말한다.

4. 빈 공간에 어떤 값(1~9)이 들어갈 수 있는지 탐색한다.

 - 찾았다면 스도쿠에 채워넣는다.

 - 채워넣을 수 있는 게 없다면 백 트래킹 한다.

 

스도쿠에 빈 공간 확인하기

1. 스도쿠 전체를 탐색하며 빈 공간(0)이 발견되면 해당 좌표를 리턴한다.

2. 스도쿠에서 빈 공간이 발견되지 않으면, None을 리턴한다.

 

빈 공간에 들어갈 수 있는 값인지 식별하기

빈 공간에 들어갈 수 있는 값인지 식별하기 위해서는 "가로 / 세로 / 3 x 3 박스"를 탐색해야 한다.

 

1. 가로 탐색

스도쿠의 column을 고정시키고 row 방향으로 이동 탐색을 진행한다.

들어가고자 하는 값이 발견되면 False를 리턴한다.

 

2. 세로 탐색

스도쿠의 row을 고정시키고 col 방향으로 이동 탐색을 진행한다.

들어가고자 하는 값이 발견되면 False를 리턴한다.

 

3. 3 x 3 박스 탐색

1. 탐색이 시작될 지점을 계산한다

2. 탐색시작 지점에서부터 3 x 3 지점을 탐색한다.

1. (7, 4) 지점이 속한 박스 영역의 시작 지점을 찾는다고 가정하자.

2. 왼쪽 그림의 경우 박스의 시작 지점은 (6, 3)이다.

3. 박스 영역의 시작 지점을 찾는 공식은 (컬럼좌표//3 * 3, 로우좌표//3 *3)이다.

("a//b"는 a와 b를 나눴을 때 몫과 나머지 중에 몫만 출력한다)

 

 

 

 

 

 


코드

import sys
# sys.stdin = open("C:/Users/JIn/PycharmProjects/coding_Test/input.txt", "rt")

def pretty_print():
    for i in range(9):
        for j in range(9):
            print(board[i][j], end='')
        print()

def find_empty():
    for i in range(9):
        for j in range(9):
            if board[i][j] == 0:
                return i, j
    return None, None

def valid(col, row, val):
    # 가로 축 점검
    for i in range(9):
        if board[col][i] == val:
            return False

    # 세로 축 점검
    for i in range(9):
        if board[i][row] == val:
            return False

    # 3x3 박스 점검
    box_col = col // 3 * 3
    box_row = row // 3 * 3
    for i in range(3):
        for j in range(3):
            if board[box_col+i][box_row+j] == val:
                return False
    return True

def solution():
    col, row = find_empty()
    if col is None:
        return True
    else:
        for i in range(1, 10):
            if valid(col, row, i):
                board[col][row] = i
                if solution():
                    return True
                board[col][row] = 0
        return False

if __name__ == '__main__':
    board = [list(map(int, input())) for _ in range(9)]
    solution()
    pretty_print()

 

 

 

10282번: 해킹

최흉최악의 해커 yum3이 네트워크 시설의 한 컴퓨터를 해킹했다! 이제 서로에 의존하는 컴퓨터들은 점차 하나둘 전염되기 시작한다. 어떤 컴퓨터 a가 다른 컴퓨터 b에 의존한다면, b가 감염되면

www.acmicpc.net


문제 설명

다익스트라 알고리즘을 사용하는 문제입니다.

 

- 첫 줄의 3 3 1는 각각 컴퓨터 수(n), 의존성 수(d), 감염원번호(c)를 의미합니다.

- 두 번째 줄부터 의존성 수(d)만큼 데이터가 주어집니다.

- 각각은 a, b, s로 "b는 a에 의존하며 s초 후에 감염된다"는 의미를 가지고 있습니다.

- 감염의 시작 c입니다.

 

 

왼쪽과 같은 그림에서 감염이 일어나는 순서는 다음과 같습니다.

- 1은 감염원이므로, 0초 후에 감염이 됩니다.

- 2는 2초 후에 감염이 됩니다

- 3은 8초가 아닌 6초 후에 감염이 됩니다.

 

 

 

각 변수의 조건 범위는 다음과 같습니다.

(1 ≤ n ≤ 10,000, 1 ≤ d ≤ 100,000, 1 ≤ c ≤ n)

(1 ≤ a, b ≤ n, a ≠ b, 0 ≤ s ≤ 1,000)

 

리턴하고자 하는 답은 감염된 컴퓨터의 수와 감염되는데 걸리는 시간입니다.

 

알고리즘 설명

1. 다익스트라 알고리즘으로 문제를 해결하기 위해 아래와 같은 형식으로 데이터를 입력 받습니다.

이 리스트를 data라고 부르겠습니다.

 

 

- 1번 컴퓨터를 2와 3이 의존합니다. 각각은 2초와 8초 후에 감염됩니다.

- 0번 컴퓨터는 없습니다. 문제풀이 편의상 1~(N+1)의 리스트를 만들었습니다.

 

 

2. 각 노드로 갈 수 있는 최소시간을 갱신할 리스트를 만듭니다. 이 리스트를 dy라고 부르겠습니다.

 

dy

 

- timepassed는 x초 후에 감염되는 컴퓨터(a)를 의미합니다.

- 0번 컴퓨터는 없습니다. 문제풀이. 편의상 만들었습니다.

 

다익스트라 알고리즘은 각 노드로 가는 최소값을 구하는 문제입니다.

각 노드는 반복적으로 실행됨으로써 최소값으로 갱신됩니다.

 

문제에서 주어진대로 반복의 시작점은 감염원 번호(c)인 1번 컴퓨터입니다.

따라서 1번 컴퓨터를 0으로 설정합니다.

나머지는 10,000,000으로 설정합니다.

 

10,000,000인 이유는 문제 조건에서 최대 컴퓨터의 개수는 10,000, 감염에 걸리는 최대 시간은 1000이라고 했습니다.

따라서 최종적으로 감염에 걸릴 수 있는 최대 시간은 10,000*1,000인 10,000,000입니다.

 

3. Heap 자료구조를 사용할 리스트를 만듭니다. 이 리스트를 q라고 부르겠습니다.

 

q

 

Heap을 위해 사용할 리스트에 (s+timePassed, a) 형태로 값을 입력받습니다.

s+timePassed는 a번 컴퓨터가 감염될 때까지 걸릴 최종 시간을 의미합니다.

 

 

예를 들어 왼쪽 그림의 경우에는,

2번 컴퓨터는 2초 후에 감염됩니다.

3번 컴퓨터는 2초 + 4초인 6초 후에 감염됩니다.

 

 

 

 

4. 반복문을 수행합니다.

(1) 1번 컴퓨터에서 감염이 시작됩니다.

 

dy

 

(2) data의 1번 컴퓨터에 있는 (2,2), (3,8)를 모조리 꺼냅니다.

 

data

 

(2-1) 2번 컴퓨터의 감염시간인 2초와 dy[1]를 더한 값은 10,000,000보다 작으므로, 

dy[2]를 2+dy[1]인 2로 갱신합니다.

 

dy

 

(2-2) 최소 값을 우선으로 리턴하기 위해 q에 값을 넣습니다.

 

q

 

(2-3) 3번 컴퓨터의 감염시간인 8초와 dy[1]를 더한 값은 10,000,000보다 작으므로, 

dy[3]을 8+dy[1]인 8로 갱신합니다.

 

dy

 

(2-4) 힙 큐 알고리즘을 위한 q에 값을 추가합니다.

 

q

 

(3) q에 있는 최소시간은 2이므로, 힙 큐 알고리즘에 의해 (2,2)를 꺼냅니다.

 

q

 

(3-1) data[2]의 값은 (3,4)입니다. 2초 + 4초인 6초는 dy[3]인 8초보다 작으므로, dy[3]을 6초로 갱신합니다.

 

dy

 

(3-2) q에 값을 추가합니다.

 

q

 

(4) q에 있는 최소시간은 6이므로 (6,3)을 꺼냅니다.

 

q

 

(4-1) data[3]에는 비교대상이 없으므로 dy, queue 리스트의 변화가 없습니다.

 

(5) q에 있는 (8,3)은 이미 dy[3]보다 크므로, 비교/갱신을 진행하지 않고 꺼냅니다.

 

(6) 답을 리턴합니다.

- 1000000을 제외하고 가장 큰 값은 최종 감염시간을 나타냅니다 : 6

- 1000000을 제외한 값의 개수는 감염된 컴퓨터의 수를 나타냅니다 : 3

 

dy

문제풀며 작성한 구상도

문제 풀면서 작성했던 구상도입니다. 혹시 위의 설명으로 부족하신 분은 참고해서 봐주세요

코드

import sys
import heapq

# sys.stdin = open("C:/Users/JIn/PycharmProjects/coding_Test/input.txt", "rt")
numTestcase = int(input())

for _ in range(numTestcase):
    n, d, c = map(int, input().split())    # n : 컴퓨터 수, d : 의존성 수, c : 감염원
    data = [[] for _ in range(n+1)]  # '인덱스'는 의존받는 대상(b), '값'은 [(x초 후 감염, 의존하는 대상(a))]
    for _ in range(d):
        a, b, s = map(int, input().split())    # 감염 방향: b -> a, s초 후 감염
        data[b].append((s, a))

    # 다익스트라 알고리즘
    dy = [100000000] * (n+1)    # 몇 초 후에 감염되는지 저장
    dy[c] = 0    # 감염원(c)은 0초 후에 감염
    q = [(0, c)]
    while q:
        timePassed, infectedCp = heapq.heappop(q)
        if dy[infectedCp] < timePassed:
            continue
        for s, a in data[infectedCp]:
            if dy[a] > s + timePassed:
                dy[a] = s + timePassed
                heapq.heappush(q, (s+timePassed, a))
    time = -1
    nCp = 0
    for i in range(1, n+1):
        if dy[i] != 100000000:
            time = max(time, dy[i])
            nCp += 1
    print(nCp, time)

 

 

이번 글은 여기를 참고하여 작성하였습니다.

따라서 해당 블로그에서의 설명을 바탕으로 알고리즘 원리만 적도록 하겠습니다.

(코드는 해당 블로그를 참고 바랍니다.)

www.acmicpc.net/problem/9251

 

 

9251번: LCS

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

www.acmicpc.net

문제 내용

두 문자열이 주어졌을 때, 공통된 수열의 최장 길이를 구하는 문제다.

즉, DBBE, BDBE 가 주어졌을 때 공통 수열이 될 수 있는 것은 "D, DB, DBE"와 "B, BB, BBE"등이 있다.

그 중 가장 긴 수열은 DBE와 BBE이므로, 답으로 3을 리턴하면 된다.

알고리즘 구상

DBBE와 BDBE를 하나씩 비교하며 공통되는 부분 수열을 구해보자.

1. D와 B      : { }

2. D와 BD    : {D}

3. D와 BDB  : {D}

4. D와 BDBE : {D}

5. DB와 B    : {B}

6. DB와 BD  : {B}, {D}

7. DB와 BDB : {D}, {B}, {D, B}

8. DB와 BDBE : {D}, {B}, {D, B}

9. DBB와 B    : {B}

10. DBB와 BD : {D}, {B}

11. DBB와 BDB : {D}, {B}, {B}, {D, B}, {B, B}

12. DBB와 BDBE : {D}, {B}, {B}, {D, B}, {B, B}

13. DBBE와 B    : {B}

14. DBBE와 BD : {D}, {B}

15. DBBE와 BDB : {B}, {D}, {B}, {D, B}

16. DBBE와 BDBE : {D}, {B}, {B}, {E}, {D, B}, {D, B, E}, {B, B, E}

이러한 과정은 Matrix를 통해 표현될 수 있다.

 

매트릭스 설명

1. 매트릭의 가장자리는 0으로 덮여있습니다.

2. 세로 축의 DBBE는 첫 번째로 인풋된 데이터를 의미합니다.

3. 가로 축의 BDBE는 두 번째로 인풋된 데이터를 의미합니다.

4. 알고리즘 코드는 맨 위에서부터 가로로 한 줄씩 진행됩니다.

 - 가로 축과 세로 축의 값이 같으면, 왼쪽 대각선 위의 값에서 1을 더해줍니다.

 - 가로 축과 세로 축의 값이 다르면, 왼쪽 또는 위의 값 중 큰 것을 받아옵니다.

 

 

 

 

 

위의 매트릭스를 분석해보자

각 칸은 왼쪽위측, 왼쪽 대각선 위로부터의 진행 방향을 갖는다.

 

가로 축과 세로 축의 값이 다르면, 왼쪽 또는 위측 가진 최장 길이를 그대로 받아오면 된다.

 

왼쪽 대각선 위의 값을 받아오지 않는 이유는, 알고리즘이 맨 위부터 가로로 한 줄씩 진행됐기 때문이다.

 

만약 가로 축과 세로 축의 값이 같다면, 왼쪽 대각선 위의 값에서 1을 더해서 가져와야 한다.

왼쪽이나 위쪽에서 값을 가져와서 1을 더한다면, 세로 축(DB)와 가로 축(BDB)의 경우 2가 된다.

(이 경우에는 2가 맞긴 하다. 어쨌든 설명을 더 들어보자)

여기서 나타낸 DB와 BDB의 최장 길이의 공통 수열로 {D, B}를 나타낸 것이지만, 동시에 {B, B}를 의미하게 되고 반례에 해당한다.

코드

밑의 블로그를 참고하길 바랍니다.

suri78.tistory.com/11

 

[백준알고리즘] 9251번: LCS -Python

[백준알고리즘] 9251번: LCS -Python https://www.acmicpc.net/problem/9251 9251번: LCS LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열..

suri78.tistory.com

 

 

 

 

유튜브로 풀이를 녹화했습니다. 아래 링크에서 확인하세요.

youtu.be/rrQ4J-GJC2k


import sys
# sys.stdin = open("C:/Users/JIn/PycharmProjects/codingtest/venv/input.txt", "rt")
n = int(input())
milks = list(map(int, input().split()))

cnt = 0
turn = 0
for i in range(n):
    if milks[i] == turn:
        cnt += 1
        turn += 1
    if turn == 3:
        turn = 0
print(cnt)

+ Recent posts