반응형

https://www.acmicpc.net/problem/10844

 

10844번: 쉬운 계단 수

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

문제 설명

5656이란 수를 보자.

이 수는 인접한 모든 자리의 차이가 1이다. 이런 수를 계단 수라고 한다.

N이 주어질 때, 길이가 N인 계단 수가 총 몇 개 있는지 구해보자. 0으로 시작하는 수는 계단수가 아니다.


풀이 과정

이 문제는 단계마다의 규칙을 찾아 점화식을 세워 구현하는 DP 문제이다.

 

길이 1짜리 계단을 생각해보면,

1 2 3 4 5 6 7 8 9 총 9개의 계단이 있다.

 

길이 2짜리 계단을 생각해보면,

10 12 21 23 32 34 43 45 ... 87 89 98 로 총 17개가 있다.

 

그렇다면 길이 3짜리 계단은,

101 121 123 212 210 232 234 ... 789 787 898 987 989 가 있다.

 

여기서 규칙을 찾아보자.

끝자리가 0이나 9라면 더 늘릴 수 있는 수가 없기 때문에 하나만 더 추가로 만들 수 있다.

 

예를 들어, 1같은 경우 끝자리 0에 1을 추가한 101 만을 만들 수 있다.

하지만, 끝자리가 0이 아닌 21 같은 경우는 1을 추가한 212나 1을 뺀 210을 만들 수 있게 된다.

 

이를 이용하면, 다음과 같은 코드를 작성할 수 있다.

fun main(args: Array<String>) {
    val n = readLine()!!.toInt()

    val dp = Array(101) { IntArray(10) }
    dp[1][0] = 0
    for(i in 1 until 10) {
        dp[1][i] = 1
    }

    for(i in 2 until n+1) {
        for(j in 0 until 10) {
            when(j) {
                0 -> dp[i][j] = dp[i-1][1]
                9 -> dp[i][j] = dp[i-1][8]
                else -> dp[i][j] = (dp[i-1][j-1] + dp[i-1][j+1]) % 1000000000
            }
        }
    }

    var ans = 0L
    dp[n].forEach { ans += it }
    print(ans%1000000000)

}

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

https://app.codility.com/programmers/lessons/5-prefix_sums/genomic_range_query/start/

 

Codility

Your browser is not supported You should use a supported browser. Read more

app.codility.com

문제 설명

A:1, C:2, G:3, T:4 이며 'A,C,G,T'로 이루어진 문자열 S를 P[i]부터 Q[i]까지 범위의 인덱스로 잘랐을 때

해당 문자열 내의 최솟값을 구하는 문제이다.

 

즉, 'CAGCCTA' 라는 문자열 S가 주어지고, P[0] = 2, Q[0] = 4 이면

자른 문자열은 GCC가 되고 여기서 최솟값은 C = 2가 된다.


풀이 과정

이 문제를 풀면서 굉장히 의아한 부분이 있다.

먼저, 문제의 의도는 Prefix Sum을 이용하는 문제이지만, 파이썬으로 풀었을 때 굉장히 간단한 방법으로 다음과 같이 풀어보았다.

 

[파이썬 코드]

def solution(S, P, Q):
    arr = []
    for i in range(len(P)):
        A = P[i]
        B = Q[i]
        tmp = S[A:B+1]

        if 'A' in tmp:
            arr.append(1)
        elif 'C' in tmp:
            arr.append(2)
        elif 'G' in tmp:
            arr.append(3)
        else:
            arr.append(4)
        
    return arr

이 코드로 시간복잡도가 O(N+M)이 나오며 효율성까지 100% 통과하며 만점을 받았다.

 

이 코드를 코틀린으로 변환하면 다음과 같다.

[코틀린 코드]

fun solution(S: String, P: IntArray, Q: IntArray): IntArray {
    val arr = IntArray(P.size){0}
    for(i in P.indices) {
        val tmp = S.substring(P[i], Q[i]+1)
        if(tmp.contains('A')) {
            arr[i] = 1
        } else if(tmp.contains('C')) {
            arr[i] = 2
        } else if(tmp.contains('G')) {
            arr[i] = 3
        } else {
            arr[i] = 4
        }
    }

    return arr
}

하지만 코틀린 코드는 시간복잡도 O(N*M)이 나오며 효율성에서 통과하지 못했다.

 

두 코드를 단순 비교했을 때는 분명히 같은 코드이며

둘다 O(N*M)으로 작동하는 것이 맞지만

왜 파이썬은 O(N+M)이 나오고, 파이썬만 통과하는지 이해가 되지 않는다.

 

전체 문자열의 길이만큼 for문을 돌기 때문에 N이 발생하고

그 안에서 파이썬은 in(O(N)), 코틀린은 contains(O(N)) 이 발생하기 때문에

결국엔 O(N*M)이 발생하는 것이 맞는 것 같은데...왜 파이썬만 O(N+M)으로써 통과되는지 모르겠다...

 

(혹시 아시는분이 계시다면 댓글 부탁드리겠습니다.)

 

단순히 언어의 작동방식 차이인지...아직까지 해결을 하지 못했다 ...

일단은 prefix sum을 이용하지 않는 방식으로 해결을 하였고

prefix sum으로 다시 풀어본 뒤 재 업로드를 하려고 한다.

반응형
반응형

https://www.acmicpc.net/problem/2579

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net

문제 설명

위 링크에 주어진 문제 설명처럼, 계단을 오르는 문제이다.

단, 3번 연속된 계단을 이어서 오를 수는 없으며

마지막 계단은 무조건 밟아야 한다.


풀이 과정

이 문제는 DP(다이나믹 프로그래밍)을 이용해 앞에서 부터 최대값을 저장해 나가야 한다.

현재 계단을 밟는다면, 이전과 이이전을 연속해서 밟으면 안된다.

 

따라서, 현재 계단의 최댓값은 현재 + 이전 + 이이이전 or 현재 + 이이전 중에 선택을 해야한다.

 

한 가지 주의해야 할 점은,

인덱스 에러가 나지 않도록 하기 위해

n = 1일때, 2일때, 3 이상일 때의 케이스를 나눠주자.

n = int(input())
stairs = []
dp = [0]*301
for i in range(n):
    stairs.append(int(input()))

if n > 2:
    dp[0] = stairs[0]
    dp[1] = stairs[0]+stairs[1]
    dp[2] = max(stairs[0] + stairs[2], stairs[1]+stairs[2])
elif n > 1:
    dp[0] = stairs[0]
    dp[1] = stairs[0] + stairs[1]
elif n == 1:
    dp[0] = stairs[0]

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

print(dp[n-1])

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

문제 설명

https://www.acmicpc.net/problem/1932

 

1932번: 정수 삼각형

첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.

www.acmicpc.net

 

주어진 그림처럼 삼각형 모양으로 숫자가 주어진다.

맨 위부터 시작해 제일 아래쪽으로 탐색을 하게되는데,

현재 지점에서 다음 지점(아래 지점)으로 이동할 때는, 왼쪽대각선과 오른쪽 대각선으로만 갈 수 있다.

 

이 방법으로 탐색했을 때, 탐색경로에 있는 숫자들을 모두 더했을 때의 최대가 되는 경로를 구하는 문제이다.


풀이 과정

이 문제는 DP(다이나믹 프로그래밍)을 이용해 앞에서부터 최댓값을 저장해나가며 푸는 문제이다.

가장 첫번째 열에 있는 숫자(7, 3, 8, 2, 4)들은 선택의 여지 없이 이전의 가장 왼쪽 수를 선택해야 한다.

예를 들어, 3번째 줄의 가장 왼쪽 숫자인 8은 2번째 줄의 3을 선택했을 때의 경로를 따라야 하며

4번째 줄의 가장 왼쪽 숫자인 2는 3번째 줄의 8을 선택했을 때의 경로를 따라야 한다.

 

그 이외의 숫자들은 이전(위) 숫자 두개 중 최댓값을 선택할 수 있다.

 

이러한 특징을 반영하여 DP배열을 2차원으로 만들어 아래와 같은 코드를 짤 수 있다.

DP[1][0]에는 1번째 줄의 0번째 수를 선택했을 때의 최댓값을 저장하고

DP[1][1]에는 1번째 줄의 1번째 수를 선택했을 때의 최댓값을 저장한다.

 

이를 파이썬 코드로 나타내면 다음과 같다.

n = int(input())

arr = []
dp = [[0]*n for _ in range(n)]
for i in range(n):
    arr.append(list(map(int, input().split())))

dp[0][0] = arr[0][0]

for i in range(1, n):
    for j in range(0, i+1):
        if j == 0:
            dp[i][j] = dp[i-1][j] + arr[i][j]
        else:
            dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + arr[i][j]

print(max(dp[n-1]))

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

https://www.acmicpc.net/problem/20057

 

20057번: 마법사 상어와 토네이도

마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을

www.acmicpc.net

문제 설명

문제의 자세한 설명은 위 링크를 참조하시길 바랍니다.

이 문제는 구현 시뮬레이션 문제로, 문제에서 요구하는 사항이 꽤 많고 상세합니다. 

빠짐없이 체크하여 어떻게 구현해야 할지 구상한 후에 구현해야 하는 문제입니다.


풀이 과정

from collections import deque

n = int(input())
graph = []
graphIdx = deque()
dx = [0, 1, 0, -1]
dy = [-1, 0, 1, 0]
d_left = [(-1, 0, 0.01), (1, 0, 0.01), (-1, -1, 0.07), (1, -1, 0.07), (-2, -1, 0.02), (2, -1, 0.02), (-1, -2, 0.10),
          (1, -2, 0.10), (0, -3, 0.05), (0, -2, 0)]
d_right = [(-1, 0, 0.01), (1, 0, 0.01), (-1, 1, 0.07), (1, 1, 0.07), (-2, 1, 0.02), (2, 1, 0.02), (-1, 2, 0.10),
           (1, 2, 0.10), (0, 3, 0.05), (0, 2, 0)]
d_up = [(0, -1, 0.01), (0, 1, 0.01), (-1, -1, 0.07), (-1, 1, 0.07), (-1, -2, 0.02), (-1, 2, 0.02), (-2, -1, 0.10),
        (-2, 1, 0.10), (-3, 0, 0.05), (-2, 0, 0)]
d_down = [(0, -1, 0.01), (0, 1, 0.01), (1, -1, 0.07), (1, 1, 0.07), (1, -2, 0.02), (1, 2, 0.02), (2, -1, 0.10),
          (2, 1, 0.10), (3, 0, 0.05), (2, 0, 0)]

for i in range(n):
    graph.append(list(map(int, input().split())))


def indexing():
    x, y = n // 2, n // 2
    depth = 0

    while True:
        for i in range(4):
            if i % 2 == 0:
                depth += 1
            for j in range(depth):
                graphIdx.append((x, y, i))
                if x == 0 and y == 0:
                    return
                x += dx[i]
                y += dy[i]


def tornado(x, y, d):
    global answer

    nx = x + dx[d]
    ny = y + dy[d]
    tmp = graph[nx][ny]
    graph[nx][ny] = 0

    outSand = 0
    total = 0

    if d == 0:
        dir = d_left
    elif d == 1:
        dir = d_down
    elif d == 2:
        dir = d_right
    else:
        dir = d_up

    for i in range(9):
        ddx = dir[i][0]
        ddy = dir[i][1]
        ratio = dir[i][2]

        if x + ddx < 0 or x + ddx >= n or y + ddy < 0 or y + ddy >= n:
            outSand += int(tmp * ratio)
            answer += int(tmp * ratio)
            continue
        else:
            graph[x + ddx][y + ddy] += int(tmp * ratio)
            total += int(tmp * ratio)

    if x + dir[9][0] < 0 or x + dir[9][0] >= n or y + dir[9][1] < 0 or y + dir[9][1] >= n:
        answer += (tmp - total - outSand)
    else:
        graph[x + dir[9][0]][y + dir[9][1]] += (tmp - total - outSand)


answer = 0
indexing()
while graphIdx:
    x, y, d = graphIdx.popleft()
    if x == 0 and y == 0:
        break
    tornado(x, y, d)

print(answer)

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

https://www.acmicpc.net/problem/20056

 

20056번: 마법사 상어와 파이어볼

첫째 줄에 N, M, K가 주어진다. 둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다. 서로 다른 두 파이어볼의 위치

www.acmicpc.net

문제 설명

문제의 자세한 설명은 위 링크를 참조하시길 바랍니다.

이 문제는 구현 시뮬레이션 문제로, 문제에서 요구하는 사항이 꽤 많고 상세합니다. 

빠짐없이 체크하여 어떻게 구현해야 할지 구상한 후에 구현해야 하는 문제입니다.


풀이 과정

① 먼저 초기의 파이어볼을 입력받은 후에 fireball라는 큐와 graph의 각 칸에 파이어볼을 삽입합니다.

fireball 큐는 현재 존재하는 파이어볼의 위치를 저장하는 큐이고

graph는 N x N 그래프(맵 전체)를 나타냅니다.

이 때, graph의 각 칸에는 그 칸에 존재하는 모든 파이어볼의 정보(질량, 방향, 속력)를 저장합니다.

 

② 파이어볼을 큐에서 꺼냅니다.

꺼낸 파이어볼의 칸으로 가서 파이어볼 정보를 꺼낸 후(pop) 이 정보를 토대로 파이어볼을 이동 시킵니다.

(이동은 옮길 칸에 정보(m, s, d)를 넣어줍니다.)

이 때, 아직 파이어볼이 합쳐질지 말지는 모르기 때문에 파이어볼큐에는 파이어볼을 넣지 않습니다.

 

③ 이동이 끝난 후, 파이어볼이 2개 이상 존재하는 칸은

문제에서 제시한 규칙대로 파이어볼을 하나로 합친 후에 4개로 나눕니다.

 

④ 나누어졌다면 바뀐 정보를 다시 해당 칸에 넣어주고, 위치는 fireball 큐에 삽입합니다.

 

⑤ 파이어볼이 1개만 존재하는 칸은 그대로 다시 파이어볼에 삽입합니다.

 

② ~ ⑤ 과정을 K번 반복한 후 정답을 출력하면 됩니다.

from collections import deque

n, m, k = map(int, input().split())
graph = [[deque() for _ in range(n)] for _ in range(n)]

dx = [-1, -1, 0, 1, 1, 1, 0, -1]
dy = [0, 1, 1, 1, 0, -1, -1, -1]
fireball = deque()

for i in range(m):
    r, c, m, s, d = map(int, input().split())
    fireball.append([r-1, c-1])
    graph[r-1][c-1].append(deque([m, s, d]))

for _ in range(k):
    for j in range(len(fireball)):
        r, c = fireball.popleft()
        m, s, d = graph[r][c].popleft()
        nr = (r + s*dx[d]) % n
        nc = (c + s*dy[d]) % n
        graph[nr][nc].append(deque([m, s, d]))

    for i in range(n):
        for j in range(n):
            if len(graph[i][j]) > 1:
                sum_m, sum_s, odd_d, even_d, cnt = 0, 0, 0, 0, 0
                while graph[i][j]:
                    m, s, d = graph[i][j].popleft()
                    sum_m += m
                    sum_s += s
                    cnt += 1
                    if d % 2 == 0:
                        even_d += 1
                    else:
                        odd_d += 1
                sum_m //= 5
                if sum_m == 0:
                    continue
                sum_s //= cnt
                if even_d == cnt or odd_d == cnt:
                    dir = [0, 2, 4, 6]
                else:
                    dir = [1, 3, 5, 7]
                for d in range(4):
                    fireball.append([i, j])
                    graph[i][j].append(deque([sum_m, sum_s, dir[d]]))
            elif len(graph[i][j]) == 1:
                fireball.append([i, j])

answer = 0
for i in range(n):
    for j in range(n):
        answer += sum(arr[0] for arr in graph[i][j])

print(answer)

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

문제 설명

https://www.acmicpc.net/problem/21609

 

21609번: 상어 중학교

상어 중학교의 코딩 동아리에서 게임을 만들었다. 이 게임은 크기가 N×N인 격자에서 진행되고, 초기에 격자의 모든 칸에는 블록이 하나씩 들어있고, 블록은 검은색 블록, 무지개 블록, 일반 블록

www.acmicpc.net

문제의 설명이 굉장히 긴 편이라 위 링크의 문제를 꼼꼼히 읽어보시고 풀이과정을 살펴보시길 추천드립니다.

일반적인 BFS/ DFS 문제보다 신경써서 구현해야 할 부분들이 훨씬 까다로운 문제이므로

과정을 하나하나 잘 살펴보면서 구현하시는 것을 추천드립니다.


풀이 과정

먼저 두 방법으로 풀면서 코드를 두 가지로 작성해보았습니다.

코드①은 구현해야 할 부분들을 그대로 나열식으로 작성한 코드이며 길이가 좀 더 깁니다.

코드②는 코드①을 좀더 깔끔하고, 중복되는 코드들을 줄여 정리한 코드입니다.

두 코드 모두 접근 방식은 같습니다.


먼저 공통되는 부분인 rotate(회전)함수와 gravity(중력)함수부터 설명드리자면,

rotate반시계(왼쪽)방향으로 90도 회전을 해야 하기 때문에

(0, 0) 좌표는 => (n-1, 0) 좌표로 이동을 하게 되고

(0, 1) 좌표는 => (n-2, 0) 좌표로 이동을 하게 됩니다.

이런식으로 직접 행렬을 그려서 비교해보면

(x, y) 좌표는 => (n-1-y, x) 좌표로 이동함을 알 수 있습니다.

 

gravity는 벽(-1)이나 경계를 만날 때 까지 아래 빈칸으로 계속 쭉 떨어지게 됩니다.

그래서 밑의 행부터 시작하여 차례대로 while문을 통해 내려갈 수 있는 곳 까지 내려가게 해줍니다.

 

이제 핵심이 되는 BFS 코드(find_group함수)입니다.

풀이①은 문제에서 조건1에 해당하는

크기가 가장 큰 블록 그룹을 찾는다. 
그러한 블록 그룹이 여러 개라면 포함된 무지개 블록의 수가 가장 많은 블록 그룹, 
그러한 블록도 여러개라면 기준 블록의 행이 가장 큰 것을, 
그 것도 여러개이면 열이 가장 큰 것을 찾는다.

이 부분을 함수 안에 같이 구현을 하였습니다.

while문을 통해 그룹을 지은 다음, 그룹의 크기가 2보다 작으면 그룹을 만들지 않고 return 합니다.

현재 만든 그룹과 최대 그룹을 비교를 해서

현재 만든 그룹이 더 크면 최대 그룹을 바꿔줍니다.

크기가 같다면, 무지개 블록의 수가 많은 블록을 최대 그룹으로 하고

그것마저 같다면 기준행, 열을 비교하여 선정을 하면 됩니다.

 

이 방법을 모두 find_group 함수 안에 순서대로 작성하였습니다.

코드①

from collections import deque

n, m = map(int, input().split())
graph = []
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

for i in range(n):
    graph.append(list(map(int, input().split())))


def find_group(a, b):
    visited = [[False] * n for _ in range(n)]
    groupQ = []
    if graph[a][b] == -1 or graph[a][b] == -2:
        return
    if graph[a][b] > 0:
        myColor = graph[a][b]
    else:
        myColor = 0
    cnt = 1
    queue = deque()
    queue.append((a, b))
    visited[a][b] = True
    while queue:
        x, y = queue.popleft()
        groupQ.append((x, y))

        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if nx < 0 or nx >= n or ny < 0 or ny >= n or visited[nx][ny]:
                continue
            if graph[nx][ny] == -1 or graph[nx][ny] == -2:
                continue
            if graph[nx][ny] > 0:
                if myColor == 0:
                    myColor = graph[nx][ny]
                elif myColor != graph[nx][ny]:
                    continue
            cnt += 1
            queue.append((nx, ny))
            visited[nx][ny] = True

    if cnt < 2:
        return

    global groupCnt, maxQ

    if cnt > groupCnt:
        groupCnt = cnt
        maxQ = groupQ
    elif cnt == groupCnt:
        thisZeroCnt, maxZeroCnt = 0, 0
        for i in range(cnt):
            if graph[groupQ[i][0]][groupQ[i][1]] == 0:
                thisZeroCnt += 1
        for i in range(groupCnt):
            if graph[maxQ[i][0]][maxQ[i][1]] == 0:
                maxZeroCnt += 1
        if thisZeroCnt == maxZeroCnt:
            groupQ.sort(key=lambda x: x[1])
            groupQ.sort(key=lambda x: x[0])
            maxQ.sort(key=lambda x: x[1])
            maxQ.sort(key=lambda x: x[0])
            gx, gy, mx, my = 0, 0, 0, 0
            for i in range(cnt):
                if graph[groupQ[i][0]][groupQ[i][1]] != 0:
                    gx = groupQ[i][0]
                    gy = groupQ[i][1]
                    break
            for i in range(groupCnt):
                if graph[maxQ[i][0]][maxQ[i][1]] != 0:
                    mx = maxQ[i][0]
                    my = maxQ[i][1]
                    break

            if gx > mx:
                maxQ = groupQ
            elif gx == mx:
                if gy > my:
                    maxQ = groupQ

        elif thisZeroCnt > maxZeroCnt:
            maxQ = groupQ

    return


def gravity():
    for i in range(n-2, -1, -1):
        for j in range(n):
            if graph[i][j] != -1:
                tmp = i
                while tmp + 1 < n and graph[tmp+1][j] == -2:
                    graph[tmp+1][j] = graph[tmp][j]
                    graph[tmp][j] = -2
                    tmp += 1


def rotate():
    newGraph = [[0] * n for _ in range(n)]
    for i in range(n):
        for j in range(n):
            newGraph[n - 1 - j][i] = graph[i][j]
    return newGraph


answer = 0

while True:
    groupCnt = 0
    maxQ = []
    for i in range(n):
        for j in range(n):
            if graph[i][j] > 0:
                find_group(i, j)
    if not maxQ:
        break
    answer += len(maxQ)**2
    for x, y in maxQ:
        graph[x][y] = -2
    gravity()
    graph = rotate()
    gravity()
print(answer)

코드 1과 다른 부분은 BFS 코드(find_group함수)입니다.

코드②은 find_group함수에서는 일단 그룹을 형성하여 그 그룹의 크기와 0(무지개)크기, 일반블록+무지개블록의 좌표를 담은 리스트를 반환을 해주었고

 

이 리스트들을 하나의 큐에 넣은 후에

정렬을 통해 최대 큐(조건에 맞는 큐)를 선정하였습니다.

 

또한, visited를 함수 안이 아닌 바깥에 선언을 함으로써, 중복으로 형성하는 그룹을 없앴고

대신, 0칸(무지개)은 다음턴에 다른 그룹들과도 형성될 수 있으므로

다시 미방문 처리를 해주어야 합니다.

 

코드②

from collections import deque

n, m = map(int, input().split())
graph = []
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

for i in range(n):
    graph.append(list(map(int, input().split())))

def find_group(a, b, color):
    queue = deque()
    queue.append([a, b])

    block_cnt, zero_cnt = 1, 0
    blocks = [[a, b]]
    zeros = []

    while queue:
        x, y = queue.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]

            if nx < 0 or nx >= n or ny < 0 or ny >= n or visited[nx][ny]:
                continue

            if graph[nx][ny] == color:
                visited[nx][ny] = True
                queue.append([nx, ny])
                block_cnt += 1
                blocks.append([nx, ny])

            elif graph[nx][ny] == 0:
                visited[nx][ny] = True
                queue.append([nx, ny])
                block_cnt += 1
                zeros.append([nx, ny])
                zero_cnt += 1

    for x, y in zeros:
        visited[x][y] = False

    return [block_cnt, zero_cnt, blocks + zeros]

def gravity():
    for i in range(n-2, -1, -1):
        for j in range(n):
            if graph[i][j] != -1:
                tmp = i
                while tmp + 1 < n and graph[tmp+1][j] == -2:
                    graph[tmp+1][j] = graph[tmp][j]
                    graph[tmp][j] = -2
                    tmp += 1

def rotate():
    newGraph = [[0] * n for _ in range(n)]
    for i in range(n):
        for j in range(n):
            newGraph[n - 1 - j][i] = graph[i][j]
    return newGraph

answer = 0

while True:
    visited = [[False]*n for _ in range(n)]
    maxQ = []
    for i in range(n):
        for j in range(n):
            if graph[i][j] > 0 and not visited[i][j]:
                visited[i][j] = True
                block = find_group(i, j, graph[i][j])
                if block[0] >= 2:
                    maxQ.append(block)
    print(maxQ)
    maxQ.sort(reverse=True)

    if not maxQ:
        break
    answer += maxQ[0][0]**2
    for x, y in maxQ[0][2]:
        graph[x][y] = -2
    gravity()
    graph = rotate()
    gravity()
    print(graph)
print(answer)

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

https://www.acmicpc.net/problem/21611

 

21611번: 마법사 상어와 블리자드

마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그, 비바라기 마법을 할 수 있다. 오늘 새로 배운 마법은 블리자드이고, 크기가 N×N인 격자에서 연습하려고 한다. N은 항상 홀수이고, (

www.acmicpc.net

문제 설명

문제의 설명이 굉장히 긴 편이라 위 링크의 문제를 꼼꼼히 읽어보시고 풀이과정을 살펴보시길 추천드립니다.

이 문제는, 특별한 알고리즘 없이 문제에서 주어진 사항을 구현하는 문제이지만

구현해야 하는 부분들이 굉장히 까다로운 문제였습니다


풀이 과정

풀이과정이 굉장히 길기 때문에, 잘 모르시는 부분을 중점으로 보시는 것을 추천드립니다.

 

① 먼저, 가운데 칸(n//2, n//2)은 상어가 있는 곳이며 여기를 시작으로 하여 토네이도처럼 돌아

각 칸에 인덱스를 붙여줘야 한다.(1, 2, 3... n*n)

이 부분은 indexing함수에서 구현하였다.

 

② 그리고 상어가 마법을 시전하는데, 이 부분은 magic함수에 구현하였다.

위 아래 왼쪽 오른쪽 4방향중에 1방향과 거리가 주어지며

마법 후 해당 칸의 숫자는 사라진다.

해당칸을 0으로 표현함으로써 사라졌음을 표시하였다.

 

③ 사라진 후에는 빈 칸만큼 뒷 칸들이 당겨야 하기 때문에 이부분은 fill_blank 함수에 구현하였다.

1번 칸부터 순서대로 돌면서 0인 칸(즉, 빈칸)을 발견하면 그 칸을 blankIdx라는 큐에 넣어준 상태로

빈 칸을 만나지 않았을 때, 큐에서 빈칸의 인덱스를 꺼내어

그 칸에 현재 칸의 수를 넣어주고, 현재 칸은 0으로(빈 상태) 만들어줌으로써 당겨주었다.

모두 당겨질 때 까지 반복한다.

 

④ 이제, 연속된 숫자가 4칸 이상 존재한다면 그 칸들을 폭발시킨다.

이 부분은 bomb 함수에 구현하였다.

처음에는 저장할 숫자가 없으므로 임시로 -1을 지정하였고

칸을 돌면서 전 칸과 같은 숫자가 발생한다면 카운트를 늘려나간다.

그러다가 저장하고 있는 숫자와 다른 수를 만났을 때, 저장하고 있는 수의 카운트가 4 이상이라면 폭발해야하므로

그 칸들을 모두 0으로 바꿔준다. 그리고 점수계산을 해야 하므로 점수도 함께 저장한다.

 

⑤ 폭발 후에는 그룹을 지어야 한다.

이 부분은 grouping 함수에 구현하였다.

 

from collections import deque

n, m = map(int, input().split())
graph = []
cmd = []
score = [0]*3

def indexing():
    x, y = n // 2, n // 2
    dx = [0, 1, 0, -1]
    dy = [-1, 0, 1, 0]
    depth = 0

    while True:
        for i in range(4):
            if i % 2 == 0:
                depth += 1
            for j in range(depth):
                x += dx[i]
                y += dy[i]
                graphIdx.append((x, y))
                if x == 0 and y == 0:
                    return


def magic(dir, dist):
    x, y = n // 2, n // 2
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]

    for i in range(dist):
        x += dx[dir]
        y += dy[dir]
        if x < 0 or x >= n or y < 0 or y >= n:
            break
        graph[x][y] = 0

    fill_blank()
    while bomb():
        fill_blank()
    grouping()


def fill_blank():
    blankIdx = deque()
    for x, y in graphIdx:
        if graph[x][y] == 0:
            blankIdx.append((x, y))
        elif graph[x][y] > 0 and blankIdx:
            nx, ny = blankIdx.popleft()
            graph[nx][ny] = graph[x][y]
            graph[x][y] = 0
            blankIdx.append((x, y))


def bomb():
    visited = deque()
    cnt = 0
    num = -1
    flag = False
    for x, y in graphIdx:
        if num == graph[x][y]:
            visited.append((x, y))
            cnt += 1
        else:
            if cnt >= 4:
                score[num-1] += cnt
                flag = True

            while visited:
                nx, ny = visited.popleft()
                if cnt >= 4:
                    graph[nx][ny] = 0

            num = graph[x][y]
            cnt = 1
            visited.append((x, y))

    return flag


def grouping():
    cnt = 1
    tmpx, tmpy = graphIdx[0]
    num = graph[tmpx][tmpy]
    nums = []

    for i in range(1, len(graphIdx)):
        x, y = graphIdx[i][0], graphIdx[i][1]
        if num == graph[x][y]:
            cnt += 1
        else:
            nums.append(cnt)
            nums.append(num)
            num = graph[x][y]
            cnt = 1

    idx = 0
    for x, y in graphIdx:
        if not nums:
            break
        graph[x][y] = nums[idx]
        idx += 1
        if idx == len(nums):
            break

for i in range(n):
    graph.append(list(map(int, input().split())))

for i in range(m):
    d, s = map(int, input().split())
    cmd.append((d, s))

graphIdx = deque()
indexing()

for a, b in cmd:
    magic(a-1, b)

answer = 0
for i in range(3):
    answer += (i+1) * score[i]

print(answer)

https://github.com/HongEunho

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형

+ Recent posts