http://boj.kr/17837 

 

17837번: 새로운 게임 2

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하�

www.acmicpc.net

 

색깔을 저장할 color 배열

체스판 위의 말들을 표시할 board

말들의 상태를 관리할 horse

 

 

보통 이런 문제를 풀 때는 board판 위에 움직여야 할 아이템들의 정보를 저장하고 board를 탐색하곤 했는데,

이번에는 보드판 따로, 아이템들의 정보 따로 저장했다.

그 이유는 0번부터 K-1번의 말이 움직일 때 그 이전의 말이 움직임이 그 다음 말에 영향을 주도록 해야 하기 때문이다.

예를 들어 낚시왕 같은 문제의 경우 이전 상어가 움직였을 때의 결과를 다른 곳에 따로 저장해야 했다. 한 사이클 동안 모든 상어가 동시에 움직인다고 했기 때문이다.

하지만 이번 문제는 말들이 순서대로 움직인다고 했기 때문에 board를 그대로 사용하고, 

한 턴마다 0~K-1번의 말들을 움직이기 위해서 horse 리스트에 따로 말들의 정보를 저장했다.

 

일단 주의해야 할 점은 파란색이거나 벽으로 막혀서 후진해야 될 때도 그 후진하려는 곳이 빨간색인지 흰색인지 구분하고 그대로 실행해줘야 한다는 것이다.

그리고 양쪽다 파란색이거나 벽으로 막혀서 움직이지 않을 때도 일단 방향은 바꿔주어야 한다.

그렇게 한번 움직임이 막힌 말은 더이상 움직이지 않을거라고 생각했는데,

말이 움직일 때 '그 위에 있는 말들'이 같이 움직이는 경우가 있으므로 왼오로 움직이던 말이 아래쪽 말에 영향을 받아서 상하로 움직일 수가 있다. 사면초가를 벗어날 수 있다는 뜻이다.

import sys


def solve():
    four = False
    for t in range(1001):
        for num_horse in range(K):  # 0~K-1번의 말을 순서대로 이동시키는 것이 한 '턴'
            r, c, direct = horse[num_horse]
            next_r = r + dx[direct]
            next_c = c + dy[direct]
            idx = board[r][c].index(num_horse)  # 체스판에서의 말의 인덱스

            if 0 <= next_r < N and 0 <= next_c < N:
                if color[next_r][next_c] == WHITE:  # h 위에 있는 모든 말이 이동하고 그 위에 쌓는다
                    board[next_r][next_c].extend(board[r][c][idx:])  # 이동하려는 말+그 위에 있는 말들을 이동시킨다
                    for _ in range(idx, len(board[r][c])):  # 말들의 정보 horse 리스트를 업데이트하고, 원래 있던 위치에서 삭제
                        horse[board[r][c][-1]] = [next_r, next_c, horse[board[r][c][-1]][DIRECT]]
                        del board[r][c][-1]
                    if len(board[next_r][next_c]) >= 4:
                        four = True
                        break
                elif color[next_r][next_c] == RED:  # h 위에 있는 말의 순서를 모두 바꾼후 같이 이동하고 그 위에 쌓는다
                    board[next_r][next_c].extend(reversed(board[r][c][idx:]))
                    for _ in range(idx, len(board[r][c])):
                        horse[board[r][c][-1]] = [next_r, next_c, horse[board[r][c][-1]][DIRECT]]
                        del board[r][c][-1]
                    if len(board[next_r][next_c]) >= 4:
                        four = True
                        break
                elif color[next_r][next_c] == BLUE:  # 방향을 반대로 바꾸고 한 칸 이동한다
                    if direct == 1 or direct == 3:
                        next_d = direct + 1
                    elif direct == 2 or direct == 4:
                        next_d = direct - 1
                    next_r, next_c = r + dx[next_d], c + dy[next_d]
                    if 0 <= next_r < N and 0 <= next_c < N:
                        if color[next_r][next_c] != BLUE:
                            if color[next_r][next_c] == WHITE:
                                board[next_r][next_c].extend(board[r][c][idx:])
                            elif color[next_r][next_c] == RED:
                                board[next_r][next_c].extend(reversed(board[r][c][idx:]))

                            for i in range(idx, len(board[r][c])):
                                horse[board[r][c][-1]] = [next_r, next_c, horse[board[r][c][-1]][DIRECT]]
                                del board[r][c][-1]
                            horse[num_horse] = [next_r, next_c, next_d]

                            if len(board[next_r][next_c]) >= 4:
                                four = True
                                break
                    horse[num_horse][DIRECT] = next_d  # 이동했건 안했던 일단 방향은 바뀐다

            else:  # 칸을 벗어나려는 경우 파랑과 같이 처리한다
                if direct == 1 or direct == 3:
                    next_d = direct + 1
                elif direct == 2 or direct == 4:
                    next_d = direct - 1
                next_r, next_c = r + dx[next_d], c + dy[next_d]
                if color[next_r][next_c] != BLUE:
                    if color[next_r][next_c] == WHITE:
                        board[next_r][next_c].extend(board[r][c][idx:])
                    elif color[next_r][next_c] == RED:
                        board[next_r][next_c].extend(reversed(board[r][c][idx:]))

                    for i in range(idx, len(board[r][c])):
                        horse[board[r][c][-1]] = [next_r, next_c, horse[board[r][c][-1]][DIRECT]]
                        del board[r][c][-1]

                    if len(board[next_r][next_c]) >= 4:
                        four = True
                        break
                horse[num_horse][DIRECT] = next_d  # 이동했건 안했던 일단 방향은 바뀐다

        if four:
            return t + 1  # t = 0부터 시작이므로 +1해서 리턴
    return -1


dx = (0, 0, 0, -1, 1)
dy = (0, 1, -1, 0, 0)
R, C, DIRECT = 0, 1, 2
WHITE, RED, BLUE = 0, 1, 2
input = sys.stdin.readline
N, K = map(int, input().split())  # N=체스판의 크기,K=말의 개수
color = [list(map(int, input().split())) for _ in range(N)]  # 색깔판
board = [[[] for _ in range(N)] for _ in range(N)]  # 체스판 위에 올라가 있는 말들이 저장될 리스트
horse = []  # 0~K-1번의 말들의 정보가 저장되는 리스트. '0'~'K-1'번을 말들의 번호라고 한다.
for k in range(K):
    i, j, d = map(int, input().split())  # row, col, 방향
    board[i-1][j-1] = [k]  # k=말의 번호. 여러 말이 저장될 수 있으므로 리스트로 저장한다
    horse.append([i-1, j-1, d])  # [row, col, 방향 d]
print(solve())

'알고리즘 > 시뮬레이션' 카테고리의 다른 글

3190번. 뱀  (0) 2020.05.04
17822번. 원판 돌리기  (0) 2020.05.02
17140번. 이차원 배열과 연산  (0) 2020.04.27
SWEA 2382번. 미생물 격리  (0) 2020.04.24
17144번. 미세먼지 안녕!  (0) 2020.04.21

https://programmers.co.kr/learn/courses/30/lessons/42895

 

 

dp[0] = N을 0번 사용해서 만들 수 있는 숫자들의 그룹

dp[1] = N을 1번 사용해서 만들 수 있는 숫자들의 그룹

dp[2] = N을 2번 사용해서 만들 수 있는 숫자들의 그룹

 

dp[3] = N을 3번 사용해서 만들 수 있는 숫자들의 그룹

        = [NNN] + dp[1]에 있는 모든 숫자들과 dp[2]에 있는 모든 숫자들을 +, -, *, / 해서 만들 수 있는 숫자들의 그룹

 

점화식:

dp(n) = [dp(i)의 모든 숫자들 + - * ÷ dp(n-i)의 모든 숫자들] (i = 1부터 n-1)

(점화식에서 55 555 5555 등의 숫자는 제외했다. 이러한 숫자들은 n마다 하나씩 있다. N을 n번 써준 숫자.)

 

문제에서 괄호까지 넣을 수 있다고 해서 헷갈렸다.

예를 들어 5를 3개 가지고 만들 수 있는 연산식 중 5 + 5 ÷ 5 가 있을 때,

(5 + 5) ÷ 5 와 5 + (5 ÷ 5)는 다른데 이걸 어떻게 고려해줘야 하나? 싶을 수도 있을 것이다.

하지만 다시 생각해보면 (5 + 5)와 (5 ÷ 5)는 5를 2개 가지고 만든 연산식이다. 

즉 저 식들을 일반화해서 생각해보면

[5를 2개 가지고 만들 수 있는 수] + - * ÷ [5를 1개 가지고 만들 수 있는 수]

[5를 1개 가지고 만들 수 있는 수] + - * ÷ [5를 2개 가지고 만들 수 있는 수]

가 되는 것이다. 그냥 두 번 쓴 게 아니다. -, ÷일 때는 식의 순서가 달라지면 결과도 달라질 수 있다.

 

j의 range를 (1, i // 2 + 1)로 잡은 이유는 

우선 dp[0]은 0이므로 볼 필요가 없다. dp[n] = dp[0] + - * ÷ dp[n]도 맞긴 한데, 어차피 다 중복되는 숫자들이라 해줄 필요가 없다.

range의 두번째 인자가 i // 2 + 1 라는 것은 j를 i // 2 까지 보겠다는 건데, 그 이유는 어차피 중복되기 때문이다.

예를 들어 dp(4) = dp(1) + - * ÷ dp(3), dp(2) + - * ÷ dp(2), dp(3) + - * ÷ dp(1) 인데 기울임 처리한 마지막 연산은 밑줄 친 첫번째 연산과 앞 뒤를 바꿔준 것 밖에는 다르지 않다. +와 *는 앞뒤가 바뀌어도 결과가 같기 때문에 신경 안 써도 되고, -와 ÷만 앞뒤로 바꿔서 해주면 된다.

 

i, j, k, m 등 요상한 변수가 많아서 헷갈릴 수 있는데 변수에 별다른 뜻이 없어서 어쩔 수가 없었다...

i = 2 부터 해서 직접 변수 안에 무엇이 들어가나 써보면 좀 이해하기 쉬울 것이다.

 

# Lvl3. N으로 표현


def solution(N, number):
    answer = 0
    dp = [0, {N}]  # N을 0번, 1번 가지고 만들 수 있는 수들의 집합
    
    if N == number:
    	return 1
        
    for i in range(2, 9):  # N을 2번 ~ 8번을 가지고 만들 수 있는 수들의 집합 구하기
        case = {int(str(N)*i)}  # N, NN, NNN ...
        for j in range(1, i // 2 + 1):  # dp(3) = [dp(1) +-*/ dp(2)] 여기서 j=1, i-j=2
            for k in dp[j]:
                for m in dp[i-j]:
                    case.add(k + m)
                    case.add(k * m)
                    
                    # j는 i의 절반까지만 구하고 연산할 때 앞뒤를 바꿔줌
                    case.add(k - m)
                    case.add(m - k)
                    if k > 0:
                        case.add(m // k)
                    if m > 0:
                        case.add(k // m)

        if number in case:
            #print(dp)
            return i
        else:
            dp.append(case)

    return -1

'알고리즘 > 동적프로그래밍' 카테고리의 다른 글

14501번. 퇴사 (DP)  (0) 2020.04.17
12865번. 평범한 배낭  (0) 2019.12.06
1912번. 연속합  (0) 2019.12.06
2565번. 전깃줄  (0) 2019.12.05
11054번. 가장 긴 바이토닉 부분 수열  (0) 2019.12.05

http://boj.kr/17822 

 

17822번: 원판 돌리기

반지름이 1, 2, ..., N인 원판이 크기가 작아지는 순으로 바닥에 놓여있고, 원판의 중심은 모두 같다. 원판의 반지름이 i이면, 그 원판을 i번째 원판이라고 한다. 각각의 원판에는 M개의 정수가 적혀있고, i번째 원판에 적힌 j번째 수의 위치는 (i, j)로 표현한다. 수의 위치는 다음을 만족한다. (i, 1)은 (i, 2), (i, M)과 인접하다. (i, M)은 (i, M-1), (i, 1)과 인접하다. (i, j)는 (i, j-1), (i, j

www.acmicpc.net

 

 

1. 원판을 2차원 배열로 생각해서 같은 원에 있는 숫자들은 같은 행에 속한 숫자들이라고 생각하고, 위아래로 접한 숫자들은 같은 열에 속한 숫자들이라고 생각한다.

행 (펼쳐준다)

2. 문제에서 주어진 일련의 과정을 몇가지로 나눈다.

 

1) 기저사례를 확인한다. 원판에 수가 남아있는지 확인해서 남아있으면 더 진행하고, 남아있지 않으면 반복문을 종료한다.

원판에 숫자가 남아있는지 추적하기 위해 N * M = remain이라는 변수를 따로 두었다. 

 

2) 각 행을 회전시킨다. 회전을 쉽게 하기 위해서 각 원판의 자료형을 list가 아닌 deque로 두었다. deque을 시계방향으로 회전하려면 deque.rotate(k), 반시계방향으로 회전하려면 deque.rotate(-k) 하면 된다. 너무 쉽다.

너무 쉬워서 찜찜하면 temp 배열을 하나 새로 만들어서 직접 위치를 계산해서 옮겨주면 된다.

 

3) 2차원 배열을 순환하면서 각 원소마다 인접한 숫자가 있는지 확인하고, 있으면 그 숫자를 지울 것이라는 표시를 담은 erase 배열에 True라고 체크한다.

문제를 처음 풀었을 때는 지문에서 각 행렬마다 어떻게 비교해야되는지 예외 케이스까지 조건을 친절하게 알려줬으므로 그대로 따라서 if-elif 문으로 비교했다. 물론 그래도 된다. 

 

하지만 어떤 문제에서는 이 문제처럼 직접 비교 조건을 알려주지 않고 그냥 인접한 숫자끼리 비교하라고만 할 수도 있다. 그럴 때는 지문에서 알려준 것처럼 i=0일 때, i=N-1일 때, 0 < i < N-1, j=0일 때, j=M-1일때, 0 < j < M-1 일때로 나눠서 코딩하기보다는 BFS나 DFS 탐색 문제처럼 동서남북 방향의 원소와 비교해야 한다고 생각하고 코딩하는 편이 빠를 것 같다. 중첩 if문으로 코딩하면 중복된 코드가 많아져서 자료형이라도 수정해야 될 때 긴 시간을 소모해야 할 수도 있다.

 

나는 미로찾기 문제를 BFS나 DFS로 풀 때처럼 dx와 dy를 미리 선언해주었다. 하지만 동서남북 방향의 네 원소와 모두 비교할 필요는 없고, →방향과 ↓방향의 원소와만 비교해도 된다. 어차피 중복되기 때문이다. 주의해야 할 점이 col은 원형 리스트이므로, col과 아래쪽 방향 next_c를 비교하려고 할 때 next_c가 범위를 벗어나면 0으로 바꿔줘야 한다. 반면 row는 원형 리스트가 아니므로 범위를 벗어나면 무시하면 된다.

 

4) 3번에서 지울 숫자들을 다 표시했으면, 이번에는 그 erase 배열을 순환하면서 True라고 체크된 숫자들을 0으로 지우고, 지웠으면 다시 erase 배열을 반복문에서 사용하기 위해서 False로 돌려놓는다. 5)번을 수행하기 위해서 지운 숫자가 있음을 표시하는 bool 변수 once_erase를 True로 체크한다.

 

5) 지운 숫자가 있음을 표시하는 bool 변수 once_erase가 False면 평균을 구해서 평균보다 작은 숫자는 +1, 큰 숫자는 -1을 해 주었다.

import sys
from collections import deque


def solve():
    remain = N * M
    for x, direct, k in move:  # x=x의 배수인 원판을 회전,direct=시계(0),반시계(1),k=몇칸 회전하는지
        if remain <= 0:  # 남아 있는 숫자가 없으면 반복문을 빠져나온 후 sum을 리턴
            break

        # x의 배수인 원판(행)을 모두 구해서 회전시킨다.
        if direct == 0:  # 시계방향
            for next_x in range(1, N+1):  # next_x = x의 배수인 원판(=행)
                if next_x % x == 0:
                    circle[next_x-1].rotate(k)
        else:  # 반시계방향
            for next_x in range(1, N+1):
                if next_x % x == 0:
                    circle[next_x-1].rotate(-k)

        # 인접한 것이 있는지 찾고, 있으면 지운다
        for row in range(N):
            for col in range(M):
                if circle[row][col]:  # 이미 지운 숫자는 보지 않는다
                    for i in range(2):  # 오른쪽, 아래쪽과 비교
                        next_r, next_c = row + dx[i], col + dy[i]
                        if next_c > M-1:  # col이 맨 끝에 도달했을 때는 next_c를 0으로 설정한다.
                            next_c = 0

                        if next_r < N:  # r은 범위를 벗어나면 그냥 무시한다
                            if circle[next_r][next_c] == circle[row][col]:
                                erase[next_r][next_c], erase[row][col] = True, True

        once_erased = False  # 지운 숫자가 있음을 체크할 bool 변수
        for row in range(N):
            for col in range(M):
                if erase[row][col]:  # 인접한 숫자가 있는 원소 (erase가 True로 체크된 원소)
                    once_erased = True
                    circle[row][col] = 0
                    remain -= 1  # 숫자를 지웠으니 남은 숫자를 의미하는 remain에서 1을 빼준다
                    erase[row][col] = False  # 다음 반복문에서 재사용하기 위해 다시 False로 돌려놓음

        # 지울 숫자가 없으면 평균을 구하고 평균보다 크면 -1, 작으면 +1
        if not once_erased:
            s = sum([sum(row) for row in circle]) / remain
            for row in range(N):
                for col in range(M):
                    if circle[row][col] > s:
                        circle[row][col] -= 1
                    elif 0 < circle[row][col] < s:
                        circle[row][col] += 1

    # 원판의 합을 리턴
    return sum([sum(row) for row in circle])


dx = (0, 1)
dy = (1, 0)
input = sys.stdin.readline
N, M, T = map(int, input().split())  # N=원판의 갯수,M=원판에 적혀있는 수의 갯수,T=회전수
circle = [deque(map(int, input().split()))for _ in range(N)]
erase = [[False for _ in range(M)] for _ in range(N)]  # 지울 숫자임을 표시하는 bool 배열
move = [list(map(int, input().split())) for _ in range(T)]
print(solve())

 

'알고리즘 > 시뮬레이션' 카테고리의 다른 글

3190번. 뱀  (0) 2020.05.04
17837번. 새로운 게임 2  (0) 2020.05.03
17140번. 이차원 배열과 연산  (0) 2020.04.27
SWEA 2382번. 미생물 격리  (0) 2020.04.24
17144번. 미세먼지 안녕!  (0) 2020.04.21

Call by Value

함수를 호출하면 그 함수의 지역변수를 할당하기 위해 스택 프레임이 생성되는데, 이 공간은 함수가 종료되면 사라진다. Call by Value로 값을 전달하는 것은 변수의 값을 복사해서 전달하는 것이다. 이 복사된 값은 함수가 종료되어 스택 프레임이 사라질 때 지역변수와 같이 사라진다. 따라서 함수 안에서 값이 변경되어도 외부에 있는 값은 변경되지 않는다.

 

 

Call by Reference

Call by Reference로 값을 전달하면 변수의 주소를 전달하게 되고, 함수 안에서 주소를 이용해 값을 변경하면 외부에 있는 값도 같이 변경된다.

 

 

Call by Assignment

파이썬에서 사용되는 방식으로, 함수에 immutable(int, float, string, tuple)한 객체를 전달하면 Call by Reference로 전달이 되나, 이를 변경하면 Call by Value로 변경되어 외부 값에 영향을 주지 않는다. 반면 mutable(한 객체를 전달하면 자동으로 Call by Reference로 전달이 된다.

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

Insertion Sort in Python (삽입 정렬)  (0) 2020.06.09
크루스칼 알고리즘 (Python)  (0) 2020.05.07
프림 알고리즘 (Python)  (0) 2020.05.07
Selection Sort (선택 정렬)  (0) 2020.04.24
Bubble Sort (버블 정렬)  (0) 2020.04.24
#include <stdio.h>
#include <stdlib.h>

struct Stack {
        struct Stack* next;
        int data;
};


int push(struct Stack* stack, int data) {
        struct Stack* newNode = malloc(sizeof(struct Stack));
        if (newNode == NULL) return -1;

        newNode->data = data;
        newNode->next = stack->next;
        stack->next = newNode;

        return 0;
}

int pop(struct Stack* stack, int* data) {
        struct Stack* popNode = stack->next; 
        if (popNode == NULL) return -1;  // pop할 노드가 NULL이면(스택에 데이터가 없음) 오류 리턴
 
        *data = popNode->data;  // pop할 데이터 저장
        stack->next = popNode->next;  // head가 가리키는 데이터를 pop할 노드가 가리키던 데이터로 이어줌
        free(popNode);  // head를 이어주었으므로 pop 노드는 삭제
        return 0;
}


/*
   element의 data와 같은 노드를 찾아서 삭제하는 함수.
   맨 앞에서부터 검색 후 삭제한다.
 */
int delete(struct Stack* stack, int element) {
        struct Stack* curPos = stack;
 
        while (curPos->next != NULL) {
                if (curPos->next->data == element) {  // 검색을 curPos의 next부터 
                        struct Stack* removeNode = curPos->next;
                        curPos->next = removeNode->next;
                        free(removeNode);
                        return 0;
                }
        }

        return -1;
}

/*
   스택을 생성하는 함수. 동적 배열을 사용하는 경우에 써야 하나,
   인터페이스의 일관성을 위해서 링크드리스트인 경우에도 추가함.
 */
int createStack(struct Stack* stack) {
        stack->next = NULL;
        return 0;
}

/*
   스택을 모두 지우는 함수
 */
int deleteStack(struct Stack* stack) {
        struct Stack* removeNode = stack->next;

        while (removeNode != NULL) {
                stack->next = removeNode->next;
                free(removeNode);
                removeNode = stack->next;
        }
}

/*
   beforeItem 다음에 item를 삽입하는 함수
 */
int insertAfter(struct Stack* stack, int beforeItem, int item) {
        struct Stack* curPos = stack->next;
 
        while (curPos) {
                if (curPos->data == beforeItem) {
                        struct Stack* newNode = malloc(sizeof(struct Stack));

                        newNode->next = curPos->next;
                        newNode->data = item;
                 
                        curPos->next = newNode;
                }
                curPos = curPos->next;
        }
        return -1;
}


/*
   모든 원소를 traverse하는 함수(디버깅용)
*/
int traverse(struct Stack* stack) {
        struct Stack* curPos = stack->next;

        while (curPos) {
                printf("%d ", curPos->data);
                curPos = curPos->next;
        }
        printf("\n");
        return 0;
}

void main() {
        printf("스택에 1, 2, 3을 넣은 후 3을 찾아서 삭제하고, pop을 실행합니다. 그리고 1 다음에 10을 삽입합니다..\n");
        struct Stack* stack = malloc(sizeof(struct Stack));

        createStack(stack);
        push(stack, 1);
        push(stack, 2);
        push(stack, 3);

        traverse(stack);
 
        struct Stack* deleteItem = malloc(sizeof(struct Stack));
        delete(stack, 3);
 
        traverse(stack);

        int data;
        pop(stack, &data);
 
        traverse(stack);

        insertAfter(stack, 1, 10);
 
        traverse(stack);
        deleteStack(stack);
        traverse(stack);
}

'알고리즘 > 자료구조' 카테고리의 다른 글

다리를 지나는 트럭 (Python)  (0) 2020.03.30

http://boj.kr/17140 

 

17140번: 이차원 배열과 연산

첫째 줄에 r, c, k가 주어진다. (1 ≤ r, c, k ≤ 100) 둘째 줄부터 3개의 줄에 배열 A에 들어있는 수가 주어진다. 배열 A에 들어있는 수는 100보다 작거나 같은 자연수이다.

www.acmicpc.net

 

 

import sys
from operator import itemgetter


def solve(A, row_num, col_num):
    for t in range(101):  # t=100까지 반복
        transpose = False  # 행렬 뒤집기
        
        if 0 <= R-1 < row_num and 0 <= C-1 < col_num and A[R-1][C-1] == K:
            return t

        if row_num > 100:  # 100이 넘어가는 경우 앞의 100까지만 본다. 
            row_num = 100  # 이 코드에서는 range를 쓰므로 슬라이싱 없이 row, col의 길이만 손봐주면 된다.
        if col_num > 100:
            col_num = 100

        if row_num < col_num:  # 행 < 열의 경우 행렬을 뒤집어 C 연산을 수행해야 한다.
            transpose = True   # Flag를 설정하고 연산 이후 뒤집은 행렬을 다시 뒤집는다.
            A = list(zip(*A))
            row_num, col_num = col_num, row_num  # 행렬을 뒤집으면 row, col의 숫자도 바뀜

        maxLine = 0  # 가장 긴 줄을 저장하고 여기에 맞춰서 0을 추가하기 위함
        
        for r in range(row_num):
            search = {}
            for c in range(col_num):
                if A[r][c] != 0 and A[r][c] not in search:
                    search[A[r][c]] = A[r].count(A[r][c])
            search = sorted(search.items(), key=itemgetter(1, 0))  # 빈도수, 숫자크기 순으로 정렬
            
            A[r] = []  # sorted로 나온 [(), (), ()]를 한 리스트에 합침
            for key, val in search:
                A[r].extend([key, val])

            maxLine = max(maxLine, len(A[r]))

        for r in range(row_num):  # maxLine만큼 0으로 채우기
            l = len(A[r])
            if l < maxLine:
                A[r].extend([0] * (maxLine-l))

        if transpose:  # C 연산의 경우 다시 행렬을 되돌림
            A = list(zip(*A))
        row_num, col_num = len(A), len(A[0])
        
    return -1


input = sys.stdin.readline
R, C, K = map(int, input().split())
A = [list(map(int, input().split())) for _ in range(3)]
print(solve(A, 3, 3))

'알고리즘 > 시뮬레이션' 카테고리의 다른 글

17837번. 새로운 게임 2  (0) 2020.05.03
17822번. 원판 돌리기  (0) 2020.05.02
SWEA 2382번. 미생물 격리  (0) 2020.04.24
17144번. 미세먼지 안녕!  (0) 2020.04.21
16235번. 나무 재테크  (0) 2020.04.20

http://boj.kr/17142 

 

17142번: 연구소 3

인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가장 처음에 모든 바이러스는 비활성 상태이고, 활성 상태인 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다. 승원이는 연구소의 바이러스 M개를 활성 상태로 변경하려고 한다. 연구소는 크기가 N×N인 정사각형으로 나타낼 수 있으며, 정사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는

www.acmicpc.net

 

 

문제가 이해가 안되서 애 먹었다.

모든 바이러스는 동서남북 방향으로 확산할 때마다 1초가 걸리는데,

비활성화된 바이러스는 활성화되도록 바꾸고/바이러스가 없는 빈칸(0)은 바이러스로 바꾼다.

문제에서 찾으라는 것은 모든 칸에 바이러스가 존재하게 될 때의 시간이다. (벽은 제외하고..)

바이러스의 활성화/비활성화 여부는 상관없고, 모든 칸에 0이 없어질 때의 시간을 찾는 것이다.

참고로 바이러스는 동서남북으로 확산할 때 무조건 1초가 걸린다. 비활성화 바이러스를 활성화시키는데는 0초, 빈칸을 바이러스로 확산시키는데는 1초가 걸린다고 생각하면 나처럼 고생한다.

 

밑의 두 예제를 보면 이해가 될 것이다.

5 1
2 2 2 1 1
2 1 1 1 1
2 1 1 1 1
2 1 1 1 1
2 2 2 1 1
# Answer: 0

2가 몇개고 1이 몇개고 어쩌구저쩌구하지만 주어지는 입력을 보면 빈 칸(0)이 없다. 그래서 답이 0초다.

 

4 2
0 1 1 0
2 1 1 2
2 1 1 2
0 1 1 0
# Answer: 2

line 3에 있는 두 개의 2를 선택했을 때의 변화는 다음과 같다.

1초: 위아래로 확산되면서 line 2의 빈 칸(0)은 바이러스로, line 4의 비활성화 바이러스(2)는 활성화 바이러스로 바꾼다.

2초: line 4의 활성화 바이러스가 된 2는 line 5의 빈 칸(0)을 바이러스로 바꾼다.

 

 

문제를 해결하기 위해서 먼저 입력을 board[N][N]에 받은 후에,

virus 리스트에는 바이러스들의 위치를 저장했고 empty_origin에는 빈 칸의 갯수를 저장했다.

virus 리스트에 저장된 것들은 모두 비활성화된 바이러스들이다.

이들 중 DFS로 총 M개를 뽑아 이를 활성화 바이러스로 선택했다.

 

그리고 뽑힌 바이러스들을 큐에 넣고 BFS 탐색을 시작한다.

BFS 탐색을 하는 이유는 모든 바이러스에서 동시에 네 방향으로 확산이 일어나기 때문이다.

depth를 시간이라고 생각하고 큐에 넣고 탐색을 돌리되, 반복문을 반복하는 조건은

큐에 바이러스가 있을 때거나 빈 칸이 아직 남아있을 때이다. 둘 중에 하나만 하면 안되고 둘다 만족해야 하는 이유는 

while empty > 0: 만 하면, 빈 칸은 남아있지만 벽으로 인해서 더이상 바이러스가 이동을 하지 못할 때 무한루프를 돌며, 큐에 뭐가 없는데 자꾸 pop을 시도하게 된다.

while queue: 만 하게 되면, 이미 빈 칸은 0개가 되었는데 비활성화된 바이러스만 남아있을 때, 비활성화된 바이러스를 활성화시키는 시간까지 카운트하게 된다.

 

그리고 어차피 큐에 시간(depth)을 넣어서 돌리는데 반복문이 종료되면 이 depth를 리턴하면 되지 굳이 time을 따로 변수로 빼서 저장해야되냐!라고 생각할수도있는데, 생각해보면 마지막 순간의 depth가 큐에 저장되는데 이를 pop하지 않으므로 따로 저장해줘야 한다.

 

# 연구소 3
import sys
from collections import deque


def bfs(selected):
    queue = deque(selected)
    #lab = deepcopy(board)
    visited = [[0 for _ in range(N)] for _ in range(N)]
    empty = empty_origin  # 남은 빈 칸의 갯수
    time = 0  # 빈 칸의 갯수를 0으로 만드는 데까지 걸린 시간

    for i, j, _ in selected:
        visited[i][j] = 1  # 초기 바이러스의 방문 표시

    while queue and empty > 0:  # 확산 중인 바이러스가 있고 & 빈 칸이 남아있으면
        r, c, depth = queue.popleft()
        for d in range(4):
            next_r, next_c = r + dx[d], c + dy[d]
            if 0 <= next_r < N and 0 <= next_c < N and not visited[next_r][next_c]:
                if board[next_r][next_c] != 1:  # 벽이 아닌데 방문하지 않음 -> 방문해야 함
                    visited[next_r][next_c] = 1
                    queue.append([next_r, next_c, depth + 1])
                    time = depth + 1

                    if board[next_r][next_c] == 0:  # 빈 칸이 바이러스가 될 때마다 empty를 줄임
                        empty -= 1

    if empty > 0:  # 확산이 끝났는데 아직도 0이 남아 있으면 불가능한 것임
        return float('inf')

    return time


def dfs(idx, cnt, selected):
    global min_value
    if cnt == M:
        min_value = min(min_value, bfs(selected))
        return
    for i in range(idx, n_virus):
        dfs(i+1, cnt+1, selected+[virus[i]])  # selected에는 뽑힌 virus의 위치가 추가됨


min_value = float('inf')  # 문제의 답. 최소 시간
input = sys.stdin.readline
N, M = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(N)]
virus = []  # 바이러스를 놓을 수 있는 위치를 저장
n_virus = 0  # 바이러스를 놓을 수 있는 위치의 갯수
empty_origin = 0  # 빈 칸의 갯수
dx = (0, 0, -1, 1)
dy = (-1, 1, 0, 0)

for i in range(N):
    for j in range(N):
        if board[i][j] == 2:  # 바이러스를 놓을 수 있는 위치를 저장
            virus.append([i, j, 0])  # row, col, depth
            n_virus += 1
        elif board[i][j] == 0:
            empty_origin += 1

dfs(0, 0, [])

if min_value >= float('inf'):
    print(-1)
else:
    print(min_value)

 

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

11725번. 트리의 부모 찾기 (Python)  (0) 2020.05.23
programmers. 네트워크 (python)  (0) 2020.05.18
15971번. 두 로봇  (0) 2020.04.21
14501번. 퇴사  (0) 2020.04.17
16236번. 아기 상어  (0) 2020.04.13

TCP는 신뢰성 있는 데이터 전송을 필요로 하기 때문에 통신이 정상적으로 이루어질 수 있는지 확인하는 과정이 필요하다. 이것을 3 way handshake 라고 한다.

 

3-way handshake

 

 

4-way handshake

'CS > 네트워크' 카테고리의 다른 글

스위칭의 필요성, 스위칭 방법의 종류  (0) 2020.06.06

+ Recent posts