읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 짝수 행 세기

문제 풀이

처음엔 순열 조합으로 쉽게 풀 수 있지 않을까 생각했지만 도저히 풀리지 않아 라이트업을 참고하였고 아주 괴랄한 dp문제임을 알 수 있었다. 아마 나 혼자 생각해서든 절대로 풀 수 없으리라 생각한다.

우선 각 열의 1의 개수에 대해 카운트하여 저장하는 배열을 하나 생성한다. 그리고 적당히 1을 선택하는 경우의 수를 구해야 하므로 이항계수를 계산하는 함수도 정의해야 한다. 행에 대해 선택할 것이고 행의 입력 제한이 300인 것으로 보아 효율적인 DP연산으로 구해야 하며 사전에 2차원 배열로 저장해두면 나중에 조회할 때 용이하겠다. R + 1 * R + 1크기의 배열을 선언해 이항계수 값을 채워주자(전체 행에 대해 c개의 행을 선택하는 경우의 수)

def solution(a):
    MOD = 10 ** 7 + 19
    R, C = len(a), len(a[0])
    pascal = [[0] * (R + 1) for _ in range(R + 1)]
    def comb(n, c):
        if n == c or c == 0:
            pascal[n][c] = 1
            return 1
        if pascal[n][c] > 0:
            return pascal[n][c] % MOD
        pascal[n][c] = (comb(n - 1, c - 1) + comb(n - 1, c)) % MOD
        return pascal[n][c]
    for i in range(R + 1):
        for j in range(i + 1):
            comb(i, j)
    cnt = []
    for j in range(C):
        o = 0
        for i in range(R):
            if a[i][j] == 1:
                o += 1
        cnt.append(o)

이제 DP 배열을 정의해야 하는데 여기가 어마어마하다. 우선 짝수행과 홀수행에 대해 개념을 숙지해야 한다.

  • 짝수행 : 행에 존재하는 1의 개수가 짝수이거나 0
  • 홀수행 : 행에 존재하는 1의 개수가 홀수(전체행 - 짝수행)

DP배열을 row는 주어진 2차월 배열의 column에 대응되며 DP배열의 col은 각 열까지에 대해 col만큼의 짝수행을 갖는 경우의 수를 의미한다. 따라서 DP 배열의 초항은 1번째 col에 대해 ${R}C{cnt}$가 될 것이다. 그릐고 짝수행의 개수는 전체 행의 개수에서 1번 열의 1의 개수를 뺀 값으로 R - ​cnt[0]이다.

이제 1번역부터 C-1번열까지 순회하되(i에서 i + 1번 열의 값 완성) DP 배열의 갱신을 위해 0부터 R까지의 짝수행을 갖는 경우의 수를 다시 순회해야 한다.

이전열의 짝수행에 대해 경우의 수가 없다면 skip하고 0부터 현재 열의 r의 개수에 대해 다음 열이 가질 수 있는 짝수행의 개수를 알 수 있다.

  • 현재 짝수행에 k개를 선택해 배치하면 홀수행이 되므로 나머지는 그대로 짝수행
  • 현재 열의 1의 개수 중 짝수행에 배분한 k개 외 나머지는 홀수행에 배치되어 짝수행이 됨

만약 다음 열에서 만들어지는 짝수행의 개수가 범위를 넘어서거나 현재 짝수행이 선택된 k보다 작으면 skip, 다음 열에 짝수행이 발생하는 경우의 수는 현재 행에서 n-k개를 선택하는 방법(n-k나 k나 이항계수 값은 똑같기 때문에 k로 바꿔도 됨) * 현재 홀수행에서 나머지 1(n - k)을 선택하는 방법을 곱한 값이며 따라서 다음 열에서 생성될 짝수행만큼 생성될 경우의 수는 이전 열의 현재 짝수행의 경우에서 짝수행이 발생할 경우의 수를 곱한 값이다.

    dp = [[0 for _ in range(R + 1)] for _ in range(C + 1)]
    dp[1][R - cnt[0]] = pascal[R][R - cnt[0]]
    for c in range(1, C):
        for r in range(R + 1):
            if dp[c][r] == 0:
                continue
            for o in range(cnt[c] + 1):
                nxt = (r - o) + (cnt[c] - o)
                if nxt > R or r < o:
                    continue
                case = (pascal[r][o] * pascal[R-r][cnt[c] - o]) % MOD
                dp[c + 1][nxt] += (dp[c][r] * case) % MOD
    return dp[C][R]

python 코드

def solution(a):
    MOD = 10 ** 7 + 19
    R, C = len(a), len(a[0])
    pascal = [[0] * (R + 1) for _ in range(R + 1)]
    def comb(n, c):
        if n == c or c == 0:
            pascal[n][c] = 1
            return 1
        if pascal[n][c] > 0:
            return pascal[n][c] % MOD
        pascal[n][c] = (comb(n - 1, c - 1) + comb(n - 1, c)) % MOD
        return pascal[n][c]
    for i in range(R + 1):
        for j in range(i + 1):
            comb(i, j)
    cnt = []
    for j in range(C):
        o = 0
        for i in range(R):
            if a[i][j] == 1:
                o += 1
        cnt.append(o)
    dp = [[0 for _ in range(R + 1)] for _ in range(C + 1)]
    dp[1][R - cnt[0]] = pascal[R][R - cnt[0]]
    for c in range(1, C):
        for r in range(R + 1):
            if dp[c][r] == 0:
                continue
            for o in range(cnt[c] + 1):
                nxt = (r - o) + (cnt[c] - o)
                if nxt > R or r < o:
                    continue
                case = (pascal[r][o] * pascal[R-r][cnt[c] - o]) % MOD
                dp[c + 1][nxt] += (dp[c][r] * case) % MOD
    return dp[C][R]

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 트리 트리오 중간값

문제 풀이

abc 노드가 있으면 a-b. b-c. c-a길이 중 중간 값을 반환해야 하는데 두 노드가 트리에서 가장 긴 겨오라면 다른 한 노드는 또 다른 가장 긴 경로에 있거나 아니면 가장 긴 경로 사이에 있을 것이다. 따라서 임의의 노드에 대해 가장 긴 경로를 갖는 노드를 찾고 해당 노드에서 가장 긴 경로를 갖는 노드들을 탐색한다. 만약 여러 개라면 중간 값은 가장 긴 경로일 것이다. 그런데 하나라면 그 노드에서 다시 가장 긴 경로를 갖는 노드를 탐색해야 한다. 왜냐하면 처음 최장 경로 탐색 지점은 해당 경로의 한 노드에 불과하므로 다른 방향의 끝점에서도 탐색해야 한다. 그 탐색 결과 여전히 하나라면 최장경로 - 1을 반환한다.

  • 입력받은 edges 정보를 hash구조에 넣어서 그래프 완성
  • 임의의 점을 기준으로 다익스트라를 끝까지 수행해 임의의 점에서 가장 멀리 있는 점 구함
  • 앞서 구한 점이 리프노드임을 보장하므로 다시 다익스트라를 끝까지 수행해 현재 리프노드에서 가장 멀리 떨어진 리프노드들을 구한다. 여러 개면 최장 경로가 2개 이상임을 의미하므로 그대로 최장 경로 길이 리턴, 1개라면 구한 노드에서 반대쪽 리프 노드가 여러 개인지 탐색을 위해 다시 다익스트라를 끝까지 수행
  • 수행 결과가 여러 개라면 처음의 리프노드 주위로 다른 리프노드가 있었음을 의미하며 그대로 최장 경로 길이 리턴, 여전히 1개라면 반드시 나머지 하나의 노드는 최장 경로 위에 있음을 의미하므로 최장 경로 길이 - 1을 반환

python 코드

from collections import defaultdict
from heapq import heappop, heappush
def solution(n, edges):
    board = defaultdict(list)
    for srt, dst in edges:
        board[srt].append(dst)
        board[dst].append(srt)
    def compute(node):
        visited = [-1 for _ in range(n + 1)]
        M = 0
        q = [(0, node)]
        visited[node] = 0
        res = []
        while q:
            cost, dst = heappop(q)
            if M == cost:
                res.append(dst)
            elif M < cost:
                res = [dst]
                M = cost
            for v in board[dst]:
                if visited[v] == -1:
                    visited[v] = cost + 1
                    heappush(q, (cost + 1, v))
        return res, M

    visited = [-1 for _ in range(n + 1)]
    visited[edges[0][0]] = 0
    q = [(0, edges[0][0])]
    res = edges[0][0]
    M = 0
    while q:
        cost, dst = heappop(q)
        if M < cost:
            M = cost
            res = dst
        for v in board[dst]:
            if visited[v] == -1:
                visited[v] = cost + 1
                heappush(q, (cost + 1, v))
    res, cost = compute(res)
    if len(res) > 1:
        return cost
    else:
        res, cost = compute(res[0])
        if len(res) == 1:
            return cost - 1
        else:
            return cost

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 2개 이하로 다른 비트  (0) 2021.09.09
Programmers. 짝수 행 세기  (0) 2021.09.09
Programmers. 약수의 개수와 덧셈  (0) 2021.09.09
Programmers. 스타 수열  (0) 2021.09.09
Programmers. 삼각 달팽이  (0) 2021.09.09

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 약수의 개수와 덧셈

문제 풀이

매우 기초적인 문제였는데 괜히 어렵게 생각해버렸다. 소인수분해한 뒤 그 빈도에 +1씩 더해 곱하면 전체 약수의 개수가 되긴 하는데 그럴 필요 없이 제곱근을 기준으로 양쪽이 대칭이라는 점을 참고해야 한다. 1부터 제곱근까지 나누되 나눈 값과 몫이 같다면 제곱근으로 + 1, 그렇지 않다면 2를 더하고 그 값이 짝수면 더하고 홀수면 빼면 된다.

python 코드

def solution(left, right):
    answer = 0
    for i in range(left, right + 1):
        cnt = 0
        for j in range(1, int(i ** 0.5) + 1):
            if i % j == 0:
                if j == i // j:
                    cnt += 1
                else:
                    cnt += 2
        if cnt % 2 == 0:
            answer += i
        else:
            answer -= i
    return answer

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 짝수 행 세기  (0) 2021.09.09
Programmers. 트리 트리오 중간값  (0) 2021.09.09
Programmers. 스타 수열  (0) 2021.09.09
Programmers. 삼각 달팽이  (0) 2021.09.09
Programmers. 쿠키구입  (0) 2021.09.06

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 스타 수열

문제 풀이

조건을 판별할 수 있는 로직을 잘 떠올려야 했다. 직관이 업으면 풀이 자체를 떠올릴 수 없는 문제라 생각한다. 어쩔 수 없이 경험이 좀 쌓여야하지 않을까.

스타 수열의 길이는 아무래도 공통 원소가 반드시 1개 이상이므로 가급적 배열 내의 원소의 개수에 종속될 것이다. 따라서 Counter 객체로 원소의 개수를 카운트한 뒤 많은 키 부터 현재 배열로 해당 원소를 교집합으로 갖는 스타 수열의 최대 길이를 산출하고 이를 모든 키에 반복하여 리턴하면 되겠다.

  • a를 카운팅하는 Counter 객체 생성(키-빈도쌍)
  • 빈도가 높은 키에 대해 반복문 정의
  • 만약 빈도 수가 이전에 계산된 스타 수열의 길이 이하라면 최댓값 갱신이 불가하므로 skip
  • idx = 0부터 탐색하되 현재 좌표와 다음 좌표의 값이 같거나 두 좌표에 탐색값이 없는 경우 다음 좌표로 진행
  • 만약 위 조건에 해당되지 않는다면 스타수열을 만들 수 있으므로 카운트 1 올리고 2개의 원소가 매칭되었므로 좌표는 2를 더한다.

python 코드

from collections import Counter
def solution(a):
    if len(a) < 4:
        return 0
    counter = Counter(a)
    answer = -1
    for x in counter.keys():
        if counter[x] <= answer:
            continue
        i = 0
        cnt = 0
        while i < len(a) - 1:
            if (a[i] == a[i + 1]) or (a[i] != x and a[i + 1] != x):
                i += 1
            else:
                i += 2
                cnt += 1
        answer = max(answer, cnt)
    return answer * 2

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 트리 트리오 중간값  (0) 2021.09.09
Programmers. 약수의 개수와 덧셈  (0) 2021.09.09
Programmers. 삼각 달팽이  (0) 2021.09.09
Programmers. 쿠키구입  (0) 2021.09.06
Programmers. 지형편집  (0) 2021.09.06

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 삼각 달팽이

문제 풀이

뭔가 겁나 우아한 알고리즘이 있을 줄 알았는데 그냥 삼각형의 각 행의 길이가 $1,2,3,\dots$식으로 증가하고 각 방향으로 움직이는 수도 n-1, n-2, ...로 줄어든다는 규칙을 참고해 2차원 배열을 선언한 뒤 좌표를 이동시켜서 구현하는 문제였다. 난이도가 쉬워서 너무 짧은 풀이만 고려한 게 패착이었다.

  • 원소를 입력할 직각 삼각형 형태의 2차원 배열을 생성한다.
  • 커서를 이동시킬 방향을 설정한다. 아래, 오른쪽, 좌상향 대각선 방향이다.
  • n부터 1까지의 값만큼 이동하되 방향을 정해서 2차월 배열의 값을 갱신한다.
  • 마지막까지 순회를 완료하면 2차원 배열의 원소를 하나의 배열에 저장해 리턴한다.

python 코드

def solution(n):
    board = []
    for i in range(1, n + 1):
        board.append([0] * i)
    direc = [(1, 0), (0, 1), (-1, -1)]
    c = [-1, 0]
    d = 0
    s = 0
    for i in range(n, 0, -1):
        for _ in range(i):
            c[0] += direc[d % 3][0]
            c[1] += direc[d % 3][1]
            s += 1
            board[c[0]][c[1]] = s
        d += 1
    answer = []
    for b in board:
        for x in b:
            answer.append(x)
    return answer

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 약수의 개수와 덧셈  (0) 2021.09.09
Programmers. 스타 수열  (0) 2021.09.09
Programmers. 쿠키구입  (0) 2021.09.06
Programmers. 지형편집  (0) 2021.09.06
Programmers. 사칙연산  (0) 2021.09.06

+ Recent posts