레이블이 코딩연습인 게시물을 표시합니다. 모든 게시물 표시
레이블이 코딩연습인 게시물을 표시합니다. 모든 게시물 표시

2020년 9월 2일 수요일

10571. 잔디 깎기 D4

막히는 문제 풀다가 푸는 요령을 좀 안것 같아서 씀.

문제의 핵심을 알아내는 것이 중요하다.. 어찌보면 당연하지만
스스로 테스트케이스 여러가지 만들어내면서 정답이 되는 조건을 생각해서 풀어냄. 작은 경우부터 만족시키면서 점점 더 크게..
# 387 ms
import sys

sys.stdin = open("input2.txt", "r")

def is_safe(now_r,now_c):
    now_e = H[now_r][now_c]
    y_bool = True
    x_bool = True

    # 아래 위
    for i in [1,3]:
        new_y = now_r + dy[i]
        new_x = now_c + dx[i]
        while 0 <= new_y < N and 0 <= new_x < M:
            if H[new_y][new_x] > now_e:
                y_bool = False
                break
            new_y += dy[i]
            new_x += dx[i]
        if not(y_bool): break

    # 왼쪽 오른쪽
    for i in [0,2]:
        new_y = now_r + dy[i]
        new_x = now_c + dx[i]
        while 0 <= new_y < N and 0 <= new_x < M:
            if H[new_y][new_x] > now_e:
                x_bool = False
            new_y += dy[i]
            new_x += dx[i]
        if not(x_bool): break

    return y_bool or x_bool

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, M = map(int, input().split())

    H = [[0] * M for _ in range(N)]
    for i in range(N):
        H[i] = list(map(int, input().split()))

    # print(*H,sep='\n')

    # 왼쪽, 아래, 오른쪽, 위
    dy = [0, 1, 0, -1]
    dx = [-1, 0, 1, 0]

    result = True
    for r in range(N):
        for c in range(M):
            if not(is_safe(r,c)):
                result = False
        if not(result): break

    result_string = "YES"
    if not(result): result_string = "NO"
    print("#{}".format(test_case), result_string)
    # ///////////////////////////////////////////////////////////////////////////////////
밑에는 핵심 파악 뒤 개선 버전
# 258 ms
import sys

sys.stdin = open("input2.txt", "r")

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, M = map(int, input().split())

    Row_max = [0] * N
    H = [[0] * M for _ in range(N)]
    for i in range(N):
        H[i] = list(map(int, input().split()))
        Row_max[i] = max(H[i])

    # print(*H,sep='\n')

    Col_max = [0] * M
    HR = [0 for _ in range(M)]
    for i, c in enumerate(zip(*H)):
        HR[i] = list(c)
        Col_max[i] = max(HR[i])

    # 왼쪽, 아래, 오른쪽, 위
    dy = [0, 1, 0, -1]
    dx = [-1, 0, 1, 0]

    # print(Row_max,Col_max)
    result = True
    for r in range(N):
        for c in range(M):
            if H[r][c] < Row_max[r] and H[r][c] < Col_max[c]:
                result = False
                break
        if not(result): break

    result_string = "YES"
    if not(result): result_string = "NO"
    print("#{}".format(test_case), result_string)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 8월 12일 수요일

3307. 최장 증가 부분 수열 D3

 예전에 완전히 같은 문제가 있었는데도 기억이 안났어서 상기시키기 위해 올림.

또 문제 조건 잘 보자. 런타임 에러 계속 떠서 왜 안되나 몰랐다.

import sys

sys.stdin = open("sample_input3.txt", "r")

### https://wayhome25.github.io/cs/2017/04/15/cs-16/
def binary_search(target):
    # data.sort()
    start = 0
    end = len(C) - 1

    while start <= end:
        mid = (start + end) // 2

        if C[mid] <= target:
            start = mid + 1
        else:
            end = mid -1

    return start

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    # 파이썬 SW문제해결 응용_최적화 - 04 동적계획법의 활용
    # 알고보니 5262. [파이썬 S/W 문제해결 최적화] 4일차 - 정렬된 부분 집합 D4 와 같은문제...
    N = int(input())
    numbers = list(map(int, input().split()))

    C = [2**31] * (N+1)
    C[0] = 0
    LIS = [1] * N

    c_index = 0
    for i,n in enumerate(numbers):
        c_index = binary_search(n)
        C[c_index] = n
        LIS[i] = c_index
        # print("i :",i,", n :",n, ", c_index :",c_index)
        # print("C :",C)
        # print("LIS :",LIS)

    print("#{}".format(test_case), max(LIS))
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 8월 11일 화요일

1860. 진기의 최고급 붕어빵 D3

사람생각으로 푸는게 잘못 되었다는걸 상기시키기 위해 올린다.

좀더 컴퓨터 적으로 머리를 굴려야 하는데.. 그게 잘 안된다.

모범적인 답안은 해당 사이트에 들어가 포인트를 소비하며 보자..

import sys

sys.stdin = open("input2.txt", "r")


T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, M, K = map(int, input().split())

    customers = sorted(list(map(int, input().split())))

    bbang = 0

    customer_index = 0

    result = "Possible"
    if min(customers) == 0:
        result = "Impossible"
    else:
        for time in range(1, max(customers)+1):
            if time % M == 0:
                bbang += K
            while customers[customer_index] == time:
                bbang -= 1
                customer_index += 1
                if bbang < 0:
                    result = "Impossible"
                    break
                if customer_index == N:
                    break


    print("#{}".format(test_case),result)
    # ///////////////////////////////////////////////////////////////////////////////////

2817. 부분 수열의 합 D3

부분수열이라고 하지만 부분집합 문제다.

처음엔 생각나는대로 문제를 풀고, 알고보니 전 강의 실습에 있던거랑 같은 문제라서 그때 사용한 방법을 이용함(https://lightsg2.blogspot.com/2020/05/5260-sw-3.html)

다 사용해서 각각 걸린 시간 비교용과 직관과 배움의 차이?

전에 비트마스크 문제를 풀었기 때문에 이것도 알아두자.


import sys

sys.stdin = open("sample_input.txt", "r")

def allPartSum(i,K,S,R):
    global result
    if i == -1:
        if S == K:
            result += 1
        return
    if S > K: return
    if R < K: return
    allPartSum(i - 1, K, S + numbers[i], R)
    allPartSum(i - 1, K, S,R - numbers[i])

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, K = map(int, input().split())
    numbers = list(map(int, input().split()))

    result = 0
    # 시간 초과
    # for i in range(1 << len(numbers)):
    #     if sum([numbers[j] for j in range(len(numbers)) if i & (1 << j)]) == K: result += 1

    # 4,138 ms
    # for i in range(1 << len(numbers)):
    #     temp_sum = 0
    #     for j in range(len(numbers)):
    #         if i & (1 << j):
    #             temp_sum += numbers[j]
    #         if temp_sum > K: break # 2,197 ms
    #     if temp_sum == K: result += 1
    #     temp_sum = 0

    # 626 ms
    allsum = sum(numbers)
    allPartSum(N-1,K,0,allsum)

    print("#{}".format(test_case), result)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 8월 3일 월요일

4615. 재미있는 오셀로 게임 D3

8방향에 대해서 푸는 문제이다.
정말 오랜만이라 상기시켜 볼려고 글 씀.

dx = [0, 1, 1, 1, 0, -1, -1, -1]
dy = [1, 1, 0, -1, -1, -1, 0, 1]
로 방향을 관리하는게 핵심임.
비슷한 문제로 
https://www.hackerrank.com/challenges/queens-attack-2/problem
가 있었다.


from collections import deque
import sys

sys.stdin = open("sample_input.txt", "r")

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, M = map(int, input().split())

    board = [[0] * (N+1) for _ in range(N+1)]

    for i in range(N+1):
        board[0][i] = 3
        board[i][0] = 3

    # 1 이 흑. 2 가 백.
    board[N//2][N//2] = 2
    board[N // 2][(N // 2)+1] = 1
    board[(N // 2)+1][N // 2] = 1
    board[(N // 2)+1][(N // 2)+1] = 2

    # print(*board,sep='\n')

    # 아래, 우측아래, 우측, 우측위, 위, 좌측위, 좌측, 좌측아래
    dx = [0, 1, 1, 1, 0, -1, -1, -1]
    dy = [1, 1, 0, -1, -1, -1, 0, 1]

    Q = deque()

    for _ in range(M):
        c, r, color = map(int, input().split())
        board[r][c] = color
        if color == 1:
            for d in range(8):
                x, y = c, r
                x += dx[d]
                y += dy[d]
                while 1 <= x <= N and 1 <= y <= N:
                    if board[y][x] == 2:
                        Q.append((x,y))
                    if board[y][x] == 1:
                        while Q:
                            x, y = Q.pop()
                            board[y][x] = 1
                        break
                    if board[y][x] == 0 or board[y][x] == 3:
                        break
                    x += dx[d]
                    y += dy[d]
                Q.clear()
        if color == 2:
            for d in range(8):
                x, y = c, r
                x += dx[d]
                y += dy[d]
                while 1 <= x <= N and 1 <= y <= N:
                    if board[y][x] == 1:
                        Q.append((x,y))
                    if board[y][x] == 2:
                        while Q:
                            x, y = Q.pop()
                            board[y][x] = 2
                        break
                    if board[y][x] == 0 or board[y][x] == 3:
                        break
                    x += dx[d]
                    y += dy[d]
                Q.clear()
        # board[r][c] = color
        # print(r,c,color)
        # print(*board, sep='\n')

    # print()
    # print(*board,sep='\n')
    bc = 0
    wc = 0
    for i in range(1,N+1):
        for j in range(1,N+1):
            if board[i][j] == 1: bc += 1
            if board[i][j] == 2: wc += 1


    print("#{}".format(test_case),bc,wc)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 5월 20일 수요일

3820. [Professional] 2일차 - 롤러코스터


새로운 정렬방법에 놀래서 나중에 활용하려고 올림.
sort시 key를 비교방식으로 한다. c++이랑 java에는 있던데 파이썬은 삭제됐더만..

주의할 점은 functools.cmp_to_key(func)의 리턴을 True나 False가 아닌 양수,0,음수로 받는다는 것. 두 비교 결과를 통해 제일 작은 건 맨 앞에, 큰 건 뒤로 오름차순 정렬은 같다.








import sys
import itertools
import functools
sys.stdin = open("sample_input.txt", "r")

def compare_rail(x,y):
    com1 = y[0]*(x[0]+x[1]) + y[1]
    com2 = x[0]*(y[0]+y[1]) + x[1]
    print(x,y,com1,com2,com1-com2)
    return com1-com2

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N = int(input())
    rail = [0] * (N)
    for i in range(N):
        a,b = map(int, input().split())
        rail[i] = (a,b)

    # rail.sort(key=lambda x:x[1]-x[0])
    cmp = functools.cmp_to_key(compare_rail)
    print(rail)
    rail.sort(key=cmp)
    print(rail)
    v = 1
    # vc = list(itertools.permutations(rail,r=5))
    # print(vc)
    for a,b in rail:
        v = ((a*v) + b) % 1000000007
        print(v)
    # v=1
    # for r in vc:
    #     v = 1
    #     print(r)
    #     for a, b in r:
    #         v = (a * v) + b
    #         print(v)
    # print(v % 1000000007)

    print("#{}".format(test_case),v % 1000000007)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 5월 18일 월요일

3814. [Professional] 평등주의


이번에도 parametric search 방법을 써서 풀어낸다. 이런곳에서도 쓰일수 있다는 느낌을 가지고 싶어서 작성.
솔직히 상식적으로 (2 ≤ N ≤ 100000, 1 ≤ K ≤ 109) 의 범위를 가지고 있는데, 정석대로 한다면 범위 K가 10억이라 도저히 안될 것 같았다. 그러니까 for i in range(k) 같은 것은 못쓴다. 100000까지는 무리없이 되기 때문에 N 크기만큼 for문을 써야 한다고 생각했다.
그럼 N만큼만 해서 어떻게 정답을 알아낼 수 있는가? 어떻하긴 계속 순회시키면서 정답을 때려 맞추는 수밖에..


여기서 주의할 테크닉은 양쪽과 비교한 크기이기 때문에 →쪽으로 순회와 ← 쪽으로 순회를 하는데 한번에 for문으로 넣었더니 의도대로 안되서 맘 편하게 for문을 2개 썻다. 어떻게 이게 문제인지 알아냈냐면 리스트 내의 원소 순서가 반전이 되도 결과는 똑같아야 되는데 결과가 다르게 나와서 눈치챘다.

이런식으로 주어진 테스트케이스로는 되는데 제출하면 안되는 경우가 너무 많다. 그래서 왜 틀렸나 점검하고 싶어도 스스로 만들어가며 점검해야 한다. 쉐도우복싱하는 느낌이다.. 

import sys

sys.stdin = open("sample_input.txt", "r")

def isAvailable(a,c,k):
    count = 0
    for i in range(len(a)-1):
        if a[i+1] - a[i] > c:
            count += a[i+1] - (a[i] + c)
            a[i+1] = a[i] + c
        # print(a,i,c,count,k)
        if count > k:
            return False
    for i in range(len(a)-1,0,-1):
        if a[i-1] - a[i] > c:
            count += a[i-1] - (a[i] + c)
            a[i-1] = a[i] + c
        # print(a, i, c, count, k)
        if count > k:
            return False
    return True

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, K = map(int, input().split())
    A = list(map(int, input().split()))
    s = 0
    e = max(A)-min(A)
    while s < e:
        c = (s+e) // 2
        if isAvailable(A[:],c,K):
            e = c
        else:
            s = c + 1

    print("#{}".format(test_case),s)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 5월 17일 일요일

3813. [Professional] 1일차 - 그래도 수명이 절반이 되어서는

원래 정직하게 찾으면 N^3 정도는 나올 것 같은데 거꾸로 정답을 가정하고 찾아가는 방법을 써서 NlogN으로 줄었다고 한다.
다른 사이트들을 보고 참고했다. 특히 코드는 그대로 따라함.
# https://ggodong.tistory.com/90
# https://herong.tistory.com/entry/SWEA-3813-%EA%B7%B8%EB%9E%98%EB%8F%84-%EC%88%98%EB%AA%85%EC%9D%B4-%EC%A0%88%EB%B0%98%EC%9D%B4-%EB%90%98%EC%96%B4%EC%84%9C%EB%8A%94



일단 주어진 배열에서 "이 값이면 정답일까?" 하고 배열에서 최소값과 최대값의 중간값을 가정하고 풀어나간다. (위 스크린샷에선 확인해볼라고 임시로 0~20으로 지정함.)
만약 임시로 잡은 중간값이 정답이라면 이거보다 더 작은값도 정답일 확률이 있으므로 end값을 반으로 줄이고 실험. 
중간값이 정답이 아니라면 중간값보다는 더 커야 하므로 start를 중간값+1로 지정하고 계산. 
이런식으로 거꾸로 역추적하는 방식을 쓰는데 이게 parametric search 인 것 같다.
코드의 count 어쩌구는 문제 조건을 참조.

솔직히 꼼수 같다. 그래도 정답만 찾으면 그만이니..

import sys

sys.stdin = open("sample_input.txt", "r")

# https://ggodong.tistory.com/90
# https://herong.tistory.com/entry/SWEA-3813-%EA%B7%B8%EB%9E%98%EB%8F%84-%EC%88%98%EB%AA%85%EC%9D%B4-%EC%A0%88%EB%B0%98%EC%9D%B4-%EB%90%98%EC%96%B4%EC%84%9C%EB%8A%94

def isAvailable(p):
    now = 1
    cont = 0
    for i in range(1,lenS+1):
        # print("i,now,cont,p,S[i]",i,now,cont,p,S[i])
        if S[i] <= p:
            cont += 1
        else:
            cont = 0
        if cont == K[now]:
            cont = 0
            now += 1
            if now > lenK:
                break
    return now > lenK

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    lenS,lenK = map(int, input().split())
    S = list(map(int, input().split()))
    K = list(map(int, input().split()))
    S.insert(0,0)
    K.insert(0, 0)
    print(S,K)

    s = min(S)
    e = max(S)
    while s<e:
        m = (s+e) // 2
        print("s,m,e",s,m,e)
        if (isAvailable(m)):
            e = m
        else:
            s = m + 1
    print(s)

    print("#{}".format(test_case),s)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 5월 11일 월요일

5286. [파이썬 SW 문제해결 최적화] 5일차 - 그래프 색칠하기 (트리 탐색 정리)

트리 탐색을 하는 김에 재귀 깊이 우선 탐색, 스택을 이용한 깊이 우선 탐색과 너비 우선 탐색 코드를 다 해보고 정리해봤다.

import sys
from collections import deque

sys.stdin = open("sample_input.txt", "r")

def color_tree(n):
    global result

    Q = deque()
    Q.append((1,[0] * (N + 1)))
    while Q:
        n, c = Q.pop()
        if n == N+1:
            # print("possible")
            result = 1
            return

        cn = [0] * (N+1)
        for t in range(1, N+1):
            if WM[n][t] == 0: continue
            else: cn[t] = c[t]
        for m in range(1,M+1):
            if m in cn: continue
            c[n] = m
            # print(n, m, c[:])
            Q.append((n+1,c[:]))

    # if n == N+1:
    #     print("possible")
    #     result = 1
    #     return
    #
    # cn = [0] * (N+1)
    # for t in range(1, N+1):
    #     if WM[n][t] == 0: continue
    #     else: cn[t] = C[t]
    #
    # for m in range(1,M+1):
    #     if m in cn: continue
    #     C[n] = m
    #     print(n, m, C)
    #     color_tree(n+1)
    #     C[n] = 0
    # return

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, E, M = map(int, input().split())
    WM = [[0]*(N+1) for _ in range(N+1)]
    for _ in range(E):
        f, s = map(int, input().split())
        WM[f][s], WM[s][f] = 1, 1

    C = [0] * (N + 1)
    result = 0
    color_tree(1)

    print("#{}".format(test_case),result)
    # ///////////////////////////////////////////////////////////////////////////////////

재귀를 이용한 깊이 우선 탐색의 경우 잘 작동했다. 단지 이 문제의 경우에는 가능한 경우의 수를 한가지만 찾으면 되기 때문에 이미 가능한 걸 알았는데도 더 찾는건 낭비인 것 같았다. 그래서 큐에 쌓는 방식을 생각하기로 했다. 또 개인적으로 재귀를 싫어하기도 한다.

Q를 만들고 쌓게 한 뒤 뒤에서부터 pop하면 깊이 우선 탐색이 되지만 사실 재귀랑 방식이 다르게 작동한다. 한번 따져 보자.

만약 이진 트리라면 깊이 h의 경우 h+1인 왼쪽 자식과 오른쪽 자식으로 들어간다. 재귀의 경우 왼쪽이 가능한 경우 바로 호다닥 자식으로 들어가 진행하는 반면 Q 스택 방식은 일단 왼쪽 자식, 오른쪽 자식을 둘 다 스택에 넣고 왼쪽자식으로 들어간다. 이런식으로 조금 다른 것 같다.

재귀는 함수가 쌓여있어서 return을 해도 계속 쌓여있던 함수가 실행되는 반면 이 스택방식은 while문을 이용하기 때문에 return 하자마자 묻지도 따지지도 않고 바로 함수를 끝내는 모습이다.
만약 경우의 수를 더 찾더라도 계속 찾고 싶으면 return 대신 continue 등을 통해 Q가 빌 때 까지 반복하면 그만이다.


Q를 popleft로 바꾸면 너비 우선 탐색이 된다.

또 스택을 이용할 때 주로 tuple을 이용해서 정보를 넘긴다. 근데 튜플 안에서 리스트를 넘겨서 저장하면 후에 그 리스트가 변경될 시 안의 내용도 같이 변경된다는 것을 알았다. 만약
A = [1,2,3]
Q.append((A))
로 저장하면 처음 Q 안에는
Q : [([1,2,3])]
로 되어있지만 만약
A[2] = 5
Q.append((A))
를 실행하면
Q: [([1,2,3]),([1,2,5])]
가 아니라
Q: [([1,2,5]),([1,2,5])]
로 되어 기존에 저장되었던 A의 리스트도 바뀐다. 이는 리스트 값을 저장할 때 파이썬도 주소값으로 저장해서 그런 것 같다. 그래서 위 코드를 잘 보면 append 할 시 그냥
Q.append((n+1,c))
가 아니라
Q.append((n+1,c[:]))
로 리스트 뒤에 [:]를 붙여 통채로 복사해서 넣으라는 표시를 추가했다.

2020년 5월 7일 목요일

5260. [파이썬 SW 문제해결 최적화] 3일차 - 부분 집합의 합

이미 부분집합에 포함된 원소의 합들 S와 포함안된 원소의 합들 R을 이용하여 한계치를 만들어 쓸떼없는 계산을 줄이는 것이다.

위 그림에서 왼쪽으로 가면 포함, 오른쪽으로 가면 미포함이다.
S가 기준점 K를 넘을 경우, 그 트리부분은 진행하지 않는다. 뭘 더하든 K 이상이기 때문이다.
또 R가 기준점 K보다 작을 경우, 그 트리 부분은 진행하지 않는다. R은 전체원소의 합에서 후보원소에서 제외한 만큼 빼는 것으로, R을 넘는 S의 합이 나올 수가 없다.
앞으로 남아있는 모든 원소를 더해도 K는 되지 못할 것이므로..


import sys

sys.stdin = open("sample_input.txt", "r")

def allPartSum(i,K,S,R):
    global result
    if i == 0:
        if S == K:
            result += 1
        return
    if S > K: return
    if R < K: return
    allPartSum(i - 1, K, S + jiphap[i], R)
    allPartSum(i - 1, K, S,R - jiphap[i])


T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N, K = map(int, input().split())
    jiphap = list(range(N+1))
    S = 0
    allsum = sum(jiphap)
    result = 0
    allPartSum(N,K,0,allsum)
    print("#{}".format(test_case),result)
    # ///////////////////////////////////////////////////////////////////////////////////

2020년 5월 6일 수요일

5255. [파이썬 SW 문제해결 최적화] 2일차 - 타일 붙이기

내가 이런 문제만 보면 머리가 나쁘다는 걸 느낀다. 이해하기가 힘들다.
일단 감각적으로 풀었는데 막상 답이 되자 당황했었다.

우선 쉽게 풀어보기 위해 재귀방법을 이용해 풀었었다. 그 뒤 동적계획법으로 리스트에 저장해서 [0]에서 [n]까지 올라가면서 구하는 방식으로 해결했다.
내가 당황했던 부분은 inputbox(n-3) + 2*inputbox(n-2) + inputbox(n-1) 수식에서 왜 (n-2)에 *3이 아닌 *2를 해야 하느냐 였다.


그림을 그리고 나서야 이해할 수 있었다. 2칸을 차지하는 부분 b(n-2)에서 ll, --, ㅁ 이렇게 각각 3개가 있는데 왜 *3이 아니고 *2인가?
이것은 1칸을 차지하는 부분 b(n-1) 에서 차지할 때 l 로 차지하는데 이것이 2칸을 차지하는 부분 b(n-2)의 ll과 중복되기 때문.
그래서 --과 ㅁ만 남아 *2를 한다.


import sys

sys.stdin = open("sample_input.txt", "r")

def inputbox(n):
    if n <= 3:
        return BoxResultList[n]

    for i in range(4,n+1):
        if BoxResultList[i] != -1: continue
        BoxResultList[i] = BoxResultList[i-1] + 2*BoxResultList[i-2] + BoxResultList[i-3]
    return BoxResultList[n]

    if n <= 0:
        return 0
    elif n == 1:
        return 1
    elif n == 2:
        return 3
    elif n == 3:
        return 6
    else:
        return inputbox(n-3) + 2*inputbox(n-2) + inputbox(n-1)


T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N = int(input())
    BoxResultList = [-1] * (N+1)
    BoxResultList[0] = 0
    BoxResultList[1] = 1
    BoxResultList[2] = 3
    BoxResultList[3] = 6
    result = inputbox(N)
    # print(result)
    # print(BoxResultList)

    print("#{}".format(test_case),result)

    # ///////////////////////////////////////////////////////////////////////////////////

업로드

https://github.com/sglee487/SW-Expert-Academy

5254. [파이썬 SW 문제해결 최적화] 1일차 - 부분 문자열

시발 뭐 이딴게 다있냐
이 문제와는 별개로 몇 군데를 찾아보니 접미어배열 자체가 유용한 듯 하다.


강의와 외부 코드를 참고해서 작성했다.
일단 set을 이용해서 꼼수 비슷하게 정답 하나 만들어놓고, 접미어 배열와 LCP배열 만들어 놓은거를 이용해서 푼 것과 비교해서 작성했다.
for i 문을 통해 A[i][1] 의 string을 보는데 A[i][1] 의 string도 앞에서부터 끊어 읽으면 set에서의 부분문자열을 읽는 것과 비슷해진다.
단지 이렇게 끊어 읽기만 하면 i의 뒤를 읽을 때 중복해서 읽는 문제가 생긴다.
가령 abac를 보면 처음엔 A[0][1]을 봐서 'abac'를 'a', 'ab', 'aba', 'abac'로 다 읽었다 치자.
그 뒤엔 A[1][1]을 보고 'ac'를 'a', 'ac'로 읽지만 이미 'a'는 중복이므로 이를 빼고 읽어야 한다.
하지만 이미 LCP를 통해 앞의 문자와 얼마나 중복되는 가를 이미 파악했다. 그래서 중복되는 만큼 더 건너뛰어서 시작하기 위해 LCP의 값을 이용한다.



import sys

# http://cd4761.blogspot.com/2017/02/trie-1.html
# http://cd4761.blogspot.com/2017/03/trie-2.html
# https://www.geeksforgeeks.org/pattern-searching-using-suffix-tree/
# http://blog.naver.com/PostView.nhn?blogId=kks227&logNo=221028710658
# https://swexpertacademy.com/main/talk/solvingTalk/boardCommuView.do?searchCondition=COMMU_DETAIL-COMMU_TITLE-NICK_NAME_TAG&commuId=AXAuybMapTADFAXq&searchKeyword=5254&orderBy=DATE_DESC&pageSize=10&pageIndex=1&&&&&&


def LongestCommonPrefix(a,b):
    i=0
    while i < min(len(a),len(b)):
        if a[i] != b[i]: break
        i += 1
    return i

sys.stdin = open("sample_input2.txt", "r")

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    # 그냥 배열로 하니까 안됨.
    # 진짜 접미어 트리 배열 써야 하는 듯..

    inp = input().split()
    n = int(inp[0])
    s = inp[1]

    print(s)
    print("n :",n)
    # 그냥 배열로 하니까 안됨.
    # 진짜 접미어 트리 배열 써야 하는 듯..
    sps = set()
    for i in range(len(s)):
        for j in range(len(s),i,-1):
            sps.add(s[i:j])
    sp = sorted(list(sps))
    print(sp)

    A = [[0] * 2 for _ in range(len(s))]
    for i in range(len(s)):
        A[i][0] = i
        A[i][1] = s[i:]
    # print(A)
    A = sorted(A,key=lambda a:a[1])
    print(A)
    LCP = [0] * len(A)
    LCP[0] = 0
    for i in range(1,len(A)):
        LCP[i] = LongestCommonPrefix(A[i-1][1],A[i][1])
    print(LCP)

    resultc = ''
    resultl = 0
    for i in range(len(A)):
        suffix_index = A[i][0]
        curstr = A[i][1]
        if n > len(curstr):
            n += LCP[i] - len(curstr)
        else:
            resultc = curstr[0]
            resultl = len(curstr[:(n+LCP[i])])
            break

    print("#{}".format(test_case),resultc,resultl)

    # ///////////////////////////////////////////////////////////////////////////////////

5250. [파이썬 SW 문제해결 구현] 7일차 - 최소 비용


다익스트라 알고리즘에 익숙해지기 위해 씀.
최소 거리부터 집합 S에 합치는 방식이다.

import sys

sys.stdin = open("sample_input.txt", "r")

def printmatrix(matrix):
    for i in matrix:
        print(i)
# https://hongsj36.github.io/2020/02/02/Ad_GraphMST/
def Dijkstra_custom(tera):
    # 원래 visited 리스트 만들어서 append 했었는데 역시 실행시간에 문제가 발생.
    # 나도 고정배열 써야지
    visited = [[False]*N for _ in range(N)]
    # d = [[1000] * N for _ in range(N)]
    d = [[float('inf')] * N for _ in range(N)]
    # Q = []
    Q = set()
    d[0][0] = 0
    # Q.append((0,0))
    Q.add((0,0))
    while True:
        # 최소 거리부터 집합 S에 합치는 다익스트라 알고리즘임.
        y,x = 0,0
        # minfuel = 1000000
        minfuel = float('inf')
        for r, c in Q:
            if d[r][c] < minfuel and not visited[r][c]:
                minfuel = d[r][c]
                y,x = r,c

        visited[y][x] = True
        Q.remove((y,x))
        if y == N-1 and x == N-1:
            return d[N-1][N-1]
        for i in range(4):
            new_y, new_x = y + dy[i], x + dx[i]
            if 0<=new_y tera[y][x]:
                    fuel = d[y][x] + tera[new_y][new_x] - tera[y][x] + 1
                else:
                    fuel = d[y][x] + 1
                if fuel < d[new_y][new_x]:
                    d[new_y][new_x] = fuel
                    Q.add((new_y,new_x))



T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N = int(input())

    tera = [list(map(int, input().split())) for _ in range(N)]

    #왼,아래,오,위
    dx = [-1,0,1,0]
    dy = [0,1,0,-1]

    result = Dijkstra_custom(tera)

    print("#{}".format(test_case),result)

    # ///////////////////////////////////////////////////////////////////////////////////

2020년 4월 28일 화요일

5208. [파이썬 SW 문제해결 구현] 5일차 - 전기버스2

내가 지금까지 한 재귀 중 제일 자랑스러워서와 발상이 좋은 것 같아 올려봄.
또 오랜만에 타블렛 좀 써보고 싶었따.
처음에 index 0부터 시작해서 일단 숫자만큼 더하고 차차 빠지는 식으로 재귀. step을 +1씩 올리며 카운트 한다. best를 저장.
차차 +2까지 1씩해서 올리지 않고 바로 +2해서 -1만큼 한 이유는 코드의 편의성과 먼저 best를 설정해서 backtracking의 기준점 설정을 좀더 빠르게 하기 위함.

import sys

sys.stdin = open("sample_input.txt", "r")

def gobus(start_index,step, end_index):
    global scn
    # print("start_index" , start_index,"step",step,"end_index",end_index)
    if step > scn:
        return
    elif start_index >= end_index:
        # print("end.. step",step, "scn",scn)
        if step < scn:
            scn = step
            # print("scn",scn)
    else:
        for i in range(start_index+N[start_index],start_index,-1):
            gobus(i,step+1,end_index)


    return

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N = list(map(int, input().split()))[1:]
    # print(N)

    scn = len(N)-1
    gobus(0,0,len(N))

    print("#{}".format(test_case),scn-1)

    # ///////////////////////////////////////////////////////////////////////////////////

2020년 4월 27일 월요일

5204. [파이썬 SW 문제해결 구현] 4일차 - 병합 정렬

나중에 연구할려고 남겨놓았다.
이 문제는 원래 list를 이용해서 merge 할 시 리스트의 재배열 문제로 시간이 오래 걸려서 문제를 못 풀게 해서 linked list로 풀게끔 유도하고 있었으나
merge 할 때 list의 크기를 고정시켜서 정보를 빼내는 방법으로 아슬아슬하게 통과한 것 같다.
나중에 node를 이용해서 만들어봐야 할 듯 하다.. 근데 어케 하지

import sys

sys.stdin = open("sample_input.txt", "r")

def merge(left, right):

    # 밑의 것은 이론에서 리스트를 사용할 경우의 병합과정이다.
    # 근데 시간이 너무 오래 결려 안되는 것 같다.
    # 리스트를 고정 길이로 만들기.
    # https://tothefullest08.github.io/algorithm/2019/08/31/1_5204_%EB%B3%91%ED%95%A9%EC%A0%95%EB%A0%AC/

    result = [0] * (len(left) + len(right))
    # 두 개의 분할된 리스트를 병합하여 result를 만듦
    i=0
    j=0
    while i < len(left) and j < len(right) :
        # 양쪽 리스트에 원소가 남아있는 경우
        # 두 서브 시르스틔 첫 원소들을 비교하여 작은 것부터 result에 추가함
        if left[i] <= right[j]:
            result[i+j] = left[i]
            i += 1
        else:
            result[i + j] = right[j]
            j += 1
    while i < len(left) :
        # 왼쪽 리스트에 원소가 남아있는 경우
        result[i+j] = left[i]
        i += 1
    while j < len(right) :
        # 오른쪽 리스트에 원소가 남아있는 경우
        result[i + j] = right[j]
        j += 1
    return result

    # 밑에가 원본. 위에가 개조 버전.

    # while len(left) > 0 and len(right) > 0 : # 양쪽 리스트에 원소가 남아있는 경우
    #     # 두 서브 시르스틔 첫 원소들을 비교하여 작은 것부터 result에 추가함
    #     if left[0] <= right[0]:
    #         result.append(left.pop(0))
    #     else:
    #         result.append(right.pop(0))
    # if len(left) > 0: # 왼쪽 리스트에 원소가 남아있는 경우
    #     result.extend(left)
    # if len(right) > 0: # 오른쪽 리스트에 원소가 남아있는 경우
    #     result.extend(right)
    # return result


def merge_sort(m):
    global iflgtr

    if len(m) <= 1:
        return m

    # 1. DIVIDE 부분
    mid = len(m) // 2
    left = m[:mid]
    right = m[mid:]

    # 리스트의 크기가 1이 될 때까지 merge_sort 재귀 호출
    left = merge_sort(left)
    right = merge_sort(right)

    if left[-1] > right[-1]:
        iflgtr += 1

    # 2. CONQUER 부분 : 분할된 리스트들 병합
    return merge(left, right)

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////
    N = int(input())
    L = list(map(int, input().split()))

    iflgtr = 0
    LS = merge_sort(L)

    print("#{}".format(test_case),LS[N//2], iflgtr)

    # ///////////////////////////////////////////////////////////////////////////////////

5110. [파이썬 SW 문제해결 기본] 7일차 - 수열 합치기

나도 참고할라고 전에 했던거 가져왔다.

노드를 추가할 때 기존에 가지고 있던 head의 노드부터 addToLast를 계속 이용해서 추가한게 아니라 head2의 독자 linkedlist를 만들어 head1의 끝의 링크를 head2의 첫번재 노드에 연결했던 것만 기억이 난다.

import sys

sys.stdin = open("sample_input.txt", "r")

class Node:
    def __init__(self, item, link=None):
        self.item = item
        self.link = link

    def addToFirst(self,data):
        global Head
        Head = Node(data,Head)

    def add(self,pre, data):
        if pre == None:
            print('error')
        else:
            pre.link = Node(data, pre.link)


    def addToLast(self,data):
        global Head
        if Head == None:
            Head = Node(data, None)
        else:
            p = Head
            while p.link != None:
                p = p.link
            p.link = Node(data, None)

    def printall(self):
        if self == None:
            print("empty")
        else:
            p = self            while p != None:
                print(p.item)
                p = p.link

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.for test_case in range(1, T + 1):
    # ///////////////////////////////////////////////////////////////////////////////////    N, M = map(int, input().split())
    numbers = list(map(int, input().split()))

    Head = None    for item in numbers:
        Node.addToLast(Head,item)


    for _ in range(M-1):
        InsertNumbers = list(map(int, input().split()))

        new_node = Node(InsertNumbers[0])
        node = new_node
        for item in InsertNumbers[1:]:
            node.link = Node(item)
            node = node.link

        new_last_node = node

        # 처음에 있을 경우        if Head.item > InsertNumbers[0]:
            new_last_node.link = Head
            Head = new_node
            continue
        # 중간에 있을 경우        MT = False        node = Head
        pre_node = None        while node != None:
            if node.item > InsertNumbers[0]:
                pre_node.link = new_node
                new_last_node.link = node
                MT = True                break            pre_node = node
            node = node.link
        if MT: continue
        # 끝까지 못 찾은 경우 (끝에 붙여야 한다)        pre_node.link = new_node

    result_list = []
    node = Head
    while node != None:
        result_list.append(node.item)
        node = node.link
    print("#{}".format(test_case),*result_list[:-11:-1])

    # ///////////////////////////////////////////////////////////////////////////////////

5203. [파이썬 SW 문제해결 구현] 3일차 - 베이비진 게임

정답률이 의외로 낮아서 올려봄.
이론에서 나온 count list 방식을 이용하여 구하는 방법을 그대로 사용하였다.
특이한 점이라면 0~9까지의 카드를 쓰기 때문에 count list 길이는 10이 되어야 할 것 같지만 12로 했다. 그래야 run과 triplet을 동시에 검사할 때 [i] == [i+1] == [i+2] 를 이용할 시 index 범위를 넘어서 조사하는 일이 생기는데 이를 일일이 if문 예외로 걸러내기 귀찮아서 그랬다.
import sys

sys.stdin = open("sample_input.txt", "r")

def is_run_or_triplet(count_list):
for i in range(len(count_list)):
if (count_list[i] >= 1) and (count_list[i+1] >= 1) and (count_list[i+2] >= 1):
return True
if (count_list[i] >= 3):
return True
return False

T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
# ///////////////////////////////////////////////////////////////////////////////////
full_list = list(map(int, input().split()))

fpc = [0] * 12
spc = [0] * 12

result = 0
for i in range(len(full_list)):
if i % 2 == 0:
fpc[full_list[i]] += 1
if is_run_or_triplet(fpc):
result = 1
break
else:
spc[full_list[i]] += 1
if is_run_or_triplet(spc):
result = 2
break

print("#{}".format(test_case),result)

# ///////////////////////////////////////////////////////////////////////////////////

2020년 4월 24일 금요일

5189. [파이썬 SW 문제해결 구현] 2일차 - 전자카트

예시에서 연결이 1-2-3-1, 1-3-2-1 이런식으로 두개라는 것을 보고 딱 봐도 순열이라고 생각했다.
[1,2,3] 개가 주어질 경우 맨 왼쪽, 맨 오른쪽은 1로 고정되어 있으니 [2,3] 끼리의 순열만을 생각한다.
import sys

sys.stdin = open("sample_input.txt", "r")

import itertools
T = int(input())
# 여러개의 테스트 케이스가 주어지므로, 각각을 처리합니다.
for test_case in range(1, T + 1):
# ///////////////////////////////////////////////////////////////////////////////////
N = int(input())

route = [list(map(int, input().split())) for _ in range(N)]

cb = list(itertools.permutations(range(2,N+1),r=N-1))

distance = []
for tup in cb:
sub_distance = 0
sub_distance += route[0][tup[0]-1]
for i in range(len(tup)-1):
sub_distance += route[tup[i] - 1][tup[i+1] - 1]
sub_distance += route[tup[len(tup)-1]-1][0]
distance.append(sub_distance)

print("#{}".format(test_case),min(distance))

# ///////////////////////////////////////////////////////////////////////////////////