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

 

6603번: 로또

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 < k < 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로

www.acmicpc.net

 

문제: 임의의 수열이 주어졌을 때, 6개의 수를 뽑을 수 있는 경우의 수를 구하는 문제

 

풀이 방법: 백트래킹

 

풀이 코드:

import sys

def backTracking(idx):
    if len(case) == 6:
        for i in range(6):
            print(case[i],end=' ')
        print()
        return

    for i in range(idx,array[0]+1):
        if len(case) > 0:
            if case[-1] >= array[i]:
                continue
        case.append(array[i])
        backTracking(idx+1)
        case.pop()
    return

while True:
    array = list(map(int,sys.stdin.readline().rstrip().split(' ')))
    if not array[0]:
        break
    case = []
    backTracking(1)
    print()

 

풀이 설명:

풀이에 핵심이 되는 backTracking 함수를 설명하고 가겠습니다.

def backTracking(idx):
    if len(case) == 6:
        for i in range(6):
            print(case[i],end=' ')
        print()
        return

    for i in range(idx,array[0]+1):
        if len(case) > 0:
            if case[-1] >= array[i]:
                continue
        case.append(array[i])
        backTracking(idx+1)
        case.pop()
    return

문제의 조건은 다음과 같습니다.

1) 6개의 수를 뽑아라

2) 사전 순으로 출력하라

 

if len(case) == 6:
    for i in range(6):
        print(case[i],end=' ')
    print()
    return

1번 조건을 만족시키기 위해, 출력할 value들을 담은 배열의 길이가 6일 경우 출력하도록 세팅하였습니다.

 

for i in range(idx,array[0]+1):
    if len(case) > 0:
        if case[-1] >= array[i]:
            continue
    case.append(array[i])
    backTracking(idx+1)
    case.pop()
return

backTracking은 다음과 같이 구성하였습니다.

먼저 2번 조건을 만족 시켜주기 위해, 뽑은 숫자를 저장하는 case 배열에 역순으로 수가 담기지 못하도록 조건문을 설정해주었습니다.

 

이후, 주어진 수열의 값을 백트래킹 기법을 통해 탐색하도록 하여 모든 경우의 수를 출력할 수 있도록 하였습니다.

 

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

 

9663번: N-Queen

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

문제: N x N 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 구하는 문제.

 

 

풀이 방법: 백 트래킹

 

 

풀이 코드:

n = int(input())
board = [0] * n
visited = [0]*n
count = 0


def checkAvailable(col):
    for i in range(col):
        if abs(board[col] - board[i]) == col - i:
            return False
    return True

def searchGraph(col):
    global count
    if col == n:
        count += 1
        return

    for i in range(n):
        if col == 0 and n % 2 == 0 and i == n/2:  # 보드가 짝수 size 일 경우, 대칭이기 때문에 *2 연산으로 연산 횟수 줄임
            return
        if visited[i]:
            continue
        board[col] = i
        if checkAvailable(col):
            visited[i] = True
            searchGraph(col+1)
            visited[i] = False

searchGraph(0)

if n % 2 == 0:
    print(count*2)
else:
    print(count)

 

풀이 설명:

board = [0] * n
visited = [0] * n

board에는 각 행에 퀸이 몇번째 열에 있는지 여부를 저장하는 배열이고, visited는 각 열에 퀸이 존재하는지 여부를 저장하는 배열입니다.

 

def checkAvailable(col):
    for i in range(col):
        if abs(board[col] - board[i]) == col - i:
            return False
    return True

checkAvailable은 퀸이 놓여질 수 있는지 여부를 판단하는 함수 입니다.

 

예를들어,  (0 , 0)과 (1 , 1)은 서로 대각선에 위치하는 점 입니다.  (4 , 5)와 (6 , 8)도 서로 대각선의 위치에 존재하는 점 입니다.

대각선에 위치하는 점들 끼리는 행의 값 차이와 열의 값 차이가 서로 같습니다.

따라서, 다음 조건을 통해 대각선 상에 위치한다면 False를 return 하도록 구성하였습니다.

 

def searchGraph(col):
    global count
    if col == n:
        count += 1
        return

    for i in range(n):
        if col == 0 and n % 2 == 0 and i == n/2:  # 보드가 짝수 size 일 경우, 대칭이기 때문에 *2 연산으로 연산 횟수 줄임
            return
        if visited[i]:
            continue
        board[col] = i
        if checkAvailable(col):
            visited[i] = True
            searchGraph(col+1)
            visited[i] = False

searchGraph 함수는 최종적으로 체스 보드에 퀸이 들어갈 수 있는 경우의 수를 탐색하는 함수 입니다.

 

한 행에 1개의 퀸만 넣는다고 하였을 때, 퀸이 체스 보드에 들어갈 수 있는 위치는 다음 조건을 만족하여야 합니다.

1. 앞선 행의 대각선 위치에 퀸이 존재하지 않을 것

2. 같은 열에 퀸이 존재하지 않을 것

 

visited 배열을 통애 같은 열 안에 퀸이 존재하는지 확인하고, 같은 열에 퀸이 존재하지 않을 경우에 대각선에 퀸이 존재하는지 확인합니다.

즉, 두 조건을 만족하면 visited에 해당 열에 퀸이 존재한다고 표시하고(True) 재귀를 통해 다음 행에 대한 searchGraph 함수를 호출합니다.

 

함수에 들어오는 행의 값이 n일 경우, 0~ n-1번째 행에 퀸이 채워졌다는 의미이므로 1개를 카운트 해줍니다.

 

추가적으로 시간을 줄이기 위해, 짝수 size의 체스판에서는 경우의 수가 좌우 대칭을 이루기 때문에 절반까지만 세고 x2를 해주는 방식을 사용하였습니다.

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

 

1167번: 트리의 지름

트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지

www.acmicpc.net

임의의 점으로 부터 가장 먼 노드를 2번 구해 지름을 구한다는 발상에 너무 감탄했던 문제입니다.

 

문제: 트리가 주어졌을 때, 트리의 지름을 구하는 문제입니다.

 

풀이 방법: 다잌스트라

 

풀이 코드:

import random
import sys
import heapq

n = int(input())

graph = [[] for _ in range(n+1)]
dist = [1E9] * (n+1)

for i in range(1,n+1):
    line = list(map(int,sys.stdin.readline().rstrip().split()))
    nowNode = line[0]

    for j in range(1,len(line)-1,2):
        graph[nowNode].append((line[j+1], line[j])) #cost, nextNode

def dijkstra(node):
    q = [(0,node)]

    while q:
        startCost, startNode = heapq.heappop(q)

        for nextCost,nextNode in graph[startNode]:
            distance = dist[startNode] + nextCost

            if dist[nextNode] > distance:
                dist[nextNode] = distance
                heapq.heappush(q,(distance,nextNode))

#임의의 점으로 부터 제일 먼 노드 X 구하기
startpos = random.randint(1,n)
dist[startpos] = 0
dijkstra(startpos)
index = dist.index(max(dist[1:]))

#X로부터 제일 먼 노드 Y 구하기
dist = [1E9] * (n+1)
dist[index] = 0
dijkstra(index)

#X-Y의 거리가 지름이 된다
print(max(dist[1:]))

 

풀이 설명:

 

먼저 입력 값을 보면,

 

1 3 2 -1

과 같이 입력이 주어질 경우, 1에서 3까지 가는 비용이 2가 된다는 의미이고

3 1 2 4 3 -1

과 같이 입력이 주어지면, 3에서 1까지 가는 비용이 2 그리고 3에서 4까지 가는 비용이 3이라는 의미입니다.

 

for i in range(1,n+1):
    line = list(map(int,sys.stdin.readline().rstrip().split()))
    nowNode = line[0]

    for j in range(1,len(line)-1,2):
        graph[nowNode].append((line[j+1], line[j])) #cost, nextNode

방향성이 없고 한줄에 한 노드로 부터 갈 수 있는 모든 노드의 정보가 주어지므로 다음과 같이 입력을 받아주었습니다.

def dijkstra(node):
    q = [(0,node)]

    while q:
        startCost, startNode = heapq.heappop(q)

        for nextCost,nextNode in graph[startNode]:
            distance = dist[startNode] + nextCost

            if dist[nextNode] > distance:
                dist[nextNode] = distance
                heapq.heappush(q,(distance,nextNode))

다음은 가장 거리가 먼 노드를 구하기 위해 다잌스트라 함수를 구현했습니다.

 

#임의의 점으로 부터 제일 먼 노드 X 구하기
startpos = random.randint(1,n)
dist[startpos] = 0
dijkstra(startpos)
index = dist.index(max(dist[1:]))

#X로부터 제일 먼 노드 Y 구하기
dist = [1E9] * (n+1)
dist[index] = 0
dijkstra(index)

#X-Y의 거리가 지름이 된다
print(max(dist[1:]))

그런 다음, 임의의점 하나로 부터 제일 먼 노드 X를 구해줍니다.

또, X로 부터 제일 먼 노드 Y를 구해줍니다.

이때, X로부터 Y까지의 거리가 트리의 지름이 되게 됩니다.

 

이 아이디어 발상에 감탄한 문제였습니다.

 

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

 

16953번: A → B

첫째 줄에 A, B (1 ≤ A < B ≤ 109)가 주어진다.

www.acmicpc.net

문제: A, B가 주어졌을 때, 2를 곱하거나 수의 가장 오른쪽에 1을 붙이는 연산을 통해 A를 B로 바꾸는데 필요한 연산 횟수를 출력하는 문제

 

풀이 방법: top- down , bfs

 

1번 풀이 코드:

 

<top-down>

import sys

a,b = map(int,sys.stdin.readline().rstrip().split())
cnt = 0
pause = 0

while b != a and b >= a:
    cnt += 1
    if b % 10 == 1:
        b //= 10
    elif b % 2 == 0:
        b //= 2
    else:
        pause = 1
        break

if not pause and a==b:
    print(cnt+1)
else:
    print(-1)

 

풀이 설명:

결과 값이 주어지므로, top - down 방식으로 구현하는 방법으로 코드를 짜보았습니다.

수의 오른쪽에 1을 붙이는 연산은 반대로 수를 10으로 나눴을 때 나머지가 1이라는 뜻입니다.

*2의 연산은 반대로 나누기 2 입니다.

 

따라서 다음과 같이 구현하였습니다.

while문의 종료조건은 b(나눠지는 수)가 a 같거나 크면서 같지 않을 때 연산이 진행되도록 하였습니다.

 

if b % 10 == 1:
    b //= 10
elif b % 2 == 0:
    b //= 2
else:
    pause = 1
    break

if문과 elif 문의 조건에 부합하지 못하는 경우는 구할 수 없는 수 이므로, pause 값을 1로 설정해주고 반복문을 종료해줍니다.

 

if not pause and a==b:
    print(cnt+1)
else:
    print(-1)

pause에 아무런 값이 설정되지 않고 a 가 b와 동일할 경우, 연산 횟수를 출력해줍니다.

 

 

2번 풀이코드: bottom-up 방식으로 bfs 입니다.

 

import sys
from collections import deque

a, b = map(int,sys.stdin.readline().rstrip().split())
deq = deque([(a,1)])
tot = -1

while len(deq) > 0:
    val, cnt = deq.popleft()
    if 2 * val <= b:
        deq.append([2*val,cnt + 1])

    if val*10+1 <= b:
        deq.append([10*val + 1 , cnt+1])

    if 2*val == b or 10*val + 1 == b:
        tot = cnt + 1
print(tot)

풀이 설명:

주어진 a 부터 b를 구해 나가는 과정입니다.

 

if 2 * val <= b:
    deq.append([2*val,cnt + 1])

if val*10+1 <= b:
    deq.append([10*val + 1 , cnt+1])

 

위와 같이, 두가지 연산에 대하여 조건을 충족할 경우 deque에 값을 추가해줍니다.

 

if 2*val == b or 10*val + 1 == b:
    tot = cnt + 1

연산을 하다보면, 원하는 값에 도달하는 경우가 있는데 이 경우 변수에 count 값을 저장하도록 하였고

만약 값에 도달하지 못하거나 값을 넘어가게 되면 while문의 종료조건을 통해 종료 되도록 설정하였습니다.

 

1번 top-down
2번 bottom-up

속도는 top-down 방식이 약 2배 빠른 것으로 나왔습니다. 아마 단순 연산이기 때문에 deque를 거치는 것이 오히려 더 느린 것이 아닐까 생각합니다.

 

 

 

 

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

 

15650번: N과 M (2)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

처음 보는 백트래킹 문제라, 고민고민 하다가 결국 풀지 못하고 백트래킹에 대해서 구글링해서 푼 문제입니다.

DFS와 다르게 되돌아간다는 개념이 정말 신기하게 다가왔던 문제였습니다.

 

문제: 두 개의 수 N,M이 주어졌을 때, 1부터 N까지 자연수 중 M개의 수로 구성할 수 있는 모든 오름차순 수열을 모두 구하는 문제입니다.

 

풀이 방법: 백트래킹

 

풀이 코드:

import sys

n, m = map(int,sys.stdin.readline().rstrip().split())

lst = []

def backTracking(start):
    if len(lst) == m:
        print(' '.join(map(str,lst)))
        return

    for i in range(start,n+1):
        if i not in lst:
            lst.append(i)
            backTracking(i+1)
            lst.pop()

backTracking(1)

 

 

 

 

 

안녕하세요. 오늘은 github non-fast-forward 에러에 대해서 정리해보고자 합니다.

 

git에 push 하려던 상황에서 해당 에러를 만났는데요.

단순히 git을 init하고 다시 재연결한다고 해서 해결되는 문제가 아니라 정리를 해보고자 합니다.

 

<non-fast-forward 발생 이유>

Git repository에 변경사항이 현재 내 코드에 반영이 안되어 있을 경우 발생하게 됩니다.

즉, 변경사항과 변경 전인 나의 코드가 push 하려는 과정에서 충돌이 발생하여 푸시가 안되는 것이지요.

 

<해결방안>

☆제일 좋은 해결 방법은 git에 push 하기전 pull을 먼저 하고 push를 하는 것입니다.

 

 

충돌이 발생했을 경우, 해결 방안은 2가지 입니다.

 

1. 강제 병합 후 push 하는 방법

--allow-unrelated-histories 옵션을 사용하여, 병합되지 않은 부분을 강제 병합시켜주는 방법입니다.

 

2. 강제로 push 하는 방법

위와 같이 master를 +master로 지정해주거나, -f / --force 옵션을 통해 강제로 push 하는 방법이 있습니다.

 

어찌됐건, 강제 한다는 것은 좋지 않으므로 항상 pull을 먼저 해주어서 레퍼지토리 변경사항을 먼저 반영해주시면 좋을 것 같습니다.

 

읽어주셔서 감사합니다.

MNIST Classification을 CNN을 통해 간단하게 구현해보고자 합니다.

 

CNN은 Convolution Neural Network의 약자로, 필터링 기법을 신경망에 적용하여 이미지를 효과적으로 처리할 수 있는 심층 신경망 입니다.

 

CNN의 신경망은 크게 Convolution layer - Max Pooling layer - Fully-Connected layer로 구성됩니다.

 

Convolution layer는 Fliter를 통해 Feature Map을 만들어 내는 계층으로, 이미지의 특징을 추출하는 계층입니다.

Max Pooling layer는 Convolution layer의 Feature Map의 크기를 줄이는 계층입니다.

마지막으로 Fully-Connected layer는 앞선 두 계층을 통해 나온 결과물을 바탕으로 분류를 하는 계층입니다.

 

그럼 코드를 한번 봐볼까요?

 

1. 필요한 Library Import

이제 코드를 구현하기 위해 필요한 Library들을 import 해줍니다.

 

2. 필요한 Hyper Parameter 정의하기

 

<하이퍼 파라미터 설명>

· batch_size: 기계를 학습시킬 때 사용하는 데이터 크기는 보통 1000만 이상이라고 합니다.

이러한 데이터를 한번에 학습시키면 메모리에 부담이 될 뿐만 아니라 너무나도 긴 시간의 연산 시간이 걸리기 때문에 데이터를 나눠서 학습시키게 됩니다. 

이때 연산 한번에 들어가게 할 데이터 크기를 'Batch Size'라고 합니다.

 

· epochs: batch_size 만큼 나누어진 모든 데이터 셋을 학습시키는 횟수를 의미합니다.

예를 들어 1 Epoch 란, 모든 데이터 셋을 신경망을 통해 한번의 순전파와 역전파 과정을 거친 것을 말합니다.

즉, 한번의 학습과정을 마친 것을 말합니다.

 

· learning_rate: 'learnig rate'란, 학습 속도 또는 학습량을 의미합니다. 한번 학습할 때마다 학습시킬 양을 정의하는 변수입니다.

 

추가적으로, iteration은 batch로 나누어진 데이터를 뜻합니다.

총 데이터가 100개, batch size = 10 이면

1 Epoch = 10 iteration이 됩니다.

 

 

3. MNIST Dataset Load

다음은 학습에 활용할 데이터셋을 가져오는 코드입니다.

 

< torchvision.datasets 함수 인자 알아보기>

· root: ' . / '과 같이 우리가 데이터를 저장하고 사용할 경로를 지정하는 인자입니다.

· train: 정의하는 데이터가 학습용인지 테스트용인지 지정하는 인자입니다.

· download: True로 설정할 경우, 지정된 경로에 해당 데이터가 없을 시 다운로드 하도록 하는 인자입니다.

· transform: 데이터를 어떤 형태로 변형할 것인지 지정하는 인자 입니다. 위 코드와 같이 transforms.ToTensor()로 지정하게 되면,

데이터를 텐서로 변형해주는 것을 의미합니다.

 

datasets 함수를 통해, 이번 코드에서 사용할 MNIST 데이터를 불러옵니다.

 

데이터 자료형을 보면 다음과 같습니다.

위 출력을 보면 Train 데이터들은 넓이와 높이가 각각 28임을 알 수 있습니다.  넓이와 높이는 뒤에서 합성곱 계층할 때 중요하니 잘 봐두시면 좋습니다.

 

4. Loader 정의하기

DataLoader는 주어진 데이터셋을 순회 가능(iterable)하도록 만들어 주며,

각 batch의 size를 정의할 수 있고 데이터 셔플 / 병렬 처리 등을 간단하게 처리할 수 있도록 해주는 함수입니다.

 

위와 같이 학습용 / 테스트용 데이터를 DataLoader를 통해 정의해주면, 학습 시 설정한 조건에 맞게 데이터를 Load 해줍니다.

<DataLoader 함수 인자 알아보기>

· dataset : DataLoader를 통해 처리할 dataset을 정의합니다.

· batch_size : 정의된 데이터를 처리할 batch size를 정의합니다.

· shuffle : 말 그대로 데이터를 섞어줄 것인지 설정합니다.

· drop_last : batch size만큼 데이터를 묶고 남은 데이터를 버릴지 설정합니다.

 
자 이제 필요한 Hyper Parameter와 데이터 셋 정의가 끝났습니다. 
본격적으로 CNN 모델 정의와 이를 바탕으로 학습 시켜보는 코드를 알아보겠습니다.
 
5. CNN Model 

 

<Model init>

init 함수는 3개의 레이어로 구성했습니다.

 

-layer1 , layer2 : Convolution layer로 Convolution 연산을 통해 특징을 추출하고, 이에 ReLU 함수를 통해 비 선형성을 부여하며, Max Pooling 을 통해 차원을 축소합니다.

 

-layer3: Fully-Connected layer로 추출된 특징을 바탕으로 분류를 진행합니다.

이때 첫번째 Linear함수의 파라미터가 4*4*64인 이유를 간단하게 설명하자면,

Convolution layer를 통과하면서 차원이 변화하게 됩니다.

 

Convolution 연산에서는 kernel의 size가 5, padding은 default 값이므로 0으로 설정되게 됩니다.

단일 채널의 차원을 계산해보면, (28,28) 차원의 채널을 위 커널로 convolution 연산을 진행해주면 output으로 나오게되는 채널의 차원이

(24,24)가 되고 max pooling을 거치면서 (12,12)가 됩니다.

 

이 과정을 한번 더 반복해주게 되면 (4,4)차원의 단일 채널이 생성됩니다.

 

우리가 2개의 Conv2d연산을 통해 output_channel 을 64개로 해주었으므로,

 총 FeatureMap의 차원은 64*4*4가 되는 것이지요. (4*4 채널이 64개)

 

따라서, Linear의 input_channel이 64*4*4가 되는 것입니다.

 

 

추가적으로 왜 비선형성을 부여해야하는지, Convolution layer에서 Max Pooling을 통해 차원 축소하는 이유는 무엇인지 간단하게 알아보겠습니다.

 

먼저 비선형성을 부여하는 이유는 선형성의 한계 때문입니다. 

f(x) = x+2 , g(x) = x^2 이라고 가정할 때, g(f(x)) = (x+2)^2 입니다.

즉, 여러개의 선형 연산은 하나의 선형 연산으로 표현이 가능하죠.

 

입력된 데이터에 아무리 많은 선형 변환을 적용하여 주어도 하나의 선형변환으로 표현할 수 있기 때문에 쌓은 레이어의 의미가 없어지는 것이죠. 복잡한 특징을 인식할 수 없는 것이죠. 

 

따라서 위와 같은 치명적인 선형성의 한계를 극복하기 위해 비선형성을 부여해주는 것입니다.

 

두번째로 Max pooling을 통해 차원을 축소하는 이유는 바로 '연산량을 감소 시켜주기 위함'입니다.

차원 축소가 없이 학습을 시키게 될 경우, parameter 수의 증가와 이를 통한 엄청난 연산량 때문에 OverFitting이 발생할 우려가 있습니다. 또한, 엄청난 연산량은 학습 속도에도 영향을 미치게 되죠.

 

따라서 Max Pooling은 차원을 축소를 바탕으로 OverFitting의 우려를 감소시키고, 학습 속도가 너무 느려지지 않도록 하기 위해 활용합니다.

 

<Model Forward>

- forward는 init에서 구현한 3개의 레이어를 순차적으로 통과하도록 구현하였습니다.

 

전결합층에는 1차원 데이터로 전달해주어야하기 때문에 layer3 호출 전에 flatten을 진행해줍니다.

 

 

6. 학습을 위한 Model, 손실함수, 최적화 함수 정의

 

손실함수는 CrossEntropy 함수를 사용할 것이고, 최적화 함수는 Adam을 사용하도록 하겠습니다.

 

7. 학습

 

각 Train data를 불러오면서, 순전파와 역전파를 수행해주었습니다.

CNN에 Data를 통과시키는 부분이 순전파

loss를 구하고, 최적화 함수를 적용시키는 부분이 역전파 과정입니다.

 

최종적으로 98%정도의 정확도가 출력되었습니다.

 

MNIST의 경우 외곽 데이터가 큰 의미를 갖지 않기에 Padding의 여부가 결과에는 크게 영향을 미치지 않습니다.

 

Conv2d 연산의 channel, kernel size, padding 등을 조정해가며 정확도가 어떻게 달라지는지 확인해보시면 좋을 것 같습니다.

 

읽어주셔서 감사합니다.

안녕하세요. 플러터를 통해서 앱 개발을 하던 중 다음과 같이 처음 보는 에러를 만나서 정리해보고자 합니다.

 

출처:https://help.apple.com/simulator/mac/9.0/index.html#/dev8a5f2aa4e

애플에서는 다음과 같은 에러가 발생하는 이유를 두가지로 설명하고 있습니다.

1. 최대 활성화 가능한 프로세스 수 초과

2. 열린 파일의 최대 수 초과

 

즉, 말 그대로 resource가 부족한 것이기 때문에, 충분한 resource만 확보해준다면 해결 가능한 문제인거 같습니다.

 

이제 해결 방안 두가지를 소개해드릴텐데요. 이 두가지 방법을 통해 잘 해결 되셨으면 좋겠습니다.

 

첫번째로는 시스템 제한 설정을 변경하는 것입니다. 최대 활성화 가능한 프로세스 수와 파일 수를 늘려주는 것이죠.

1. Terminal을 실행시킵니다.

2. sudo launchctl limit 명령어를 실행 시킵니다.

 

2번 명령어를 실행 시키시면, 현재 최대 프로세스 수는 2000, 파일 수는 256개 인 것을 보실 수 있습니다.

 

3. sudo launchctl limit maxproc 2000 2500  : 최대 프로세스 수를 2000 -> 2500으로 변경하는 명령어입니다.

4. sudo launchctl limit maxfiles 2000 unlimited : 최대 파일 수를 256 unlimited -> 2000 unlimited로 변경하는 명령어입니다.

5. 컴퓨터 재부팅 후, 2번 명령어를 재입력해주시면 변경되신 것을 확인하실 수 있으실겁니다.

 

 

두번째로는, 불필요한 파일 / 캐시 삭제를 통해 메모리 확보하기 입니다. 

제가 활용했던 방법인데요.

순서는 다음과 같습니다.

 

1. 왼쪽 상단의 사과 > 이 Mac에 관하여 > 저장 공간 > 관리 > 개발자 로 들어가시게 되면 아래와 같이 Macintosh HD가 나오게 됩니다.

왼쪽 개발자를 들어가셔서 '캐시' 부분을 삭제해줍니다.

 

 

2. 휴지통을 비워줍니다.

 

3. 불필요한 파일, 어플리케이션을 지워줍니다.

 

사실 2번까지만 해도 충분하게 메모리를 충분하게 비울 수 있으실텐데,

그래도 안돌아갈 경우 3번까지 실행해보시고 에뮬레이터를 다시 실행하면 잘 동작하는 것을 볼 수 있으실겁니다.

 

위 방법으로 잘 해결 되셨으면 좋겠습니다 :) 

혹시나 추가로 궁금한 점 등이 있으실 경우, 댓글로 남겨주시면 답변 남겨드리겠습니다.

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

 

13172번: Σ

모듈러가 11에서 1,000,000,007이 되어 답이 달라졌지만, 역시 3을 곱한 다음 1,000,000,007으로 나눈 나머지는 7이 된다.

www.acmicpc.net

문제: 모든 주사위를 한 번씩 던졌을 때 나온 숫자들의 합의 기댓값을 구하는 문제입니다.

 

풀이 방법: 분할정복

 

풀이 코드

import sys
import math

n = int(input())
modular = 1000000007
total = 0
numerator = []
dominator = []


def getLcd(dominator:list):
    dom = dominator[0]
    for i in range(len(dominator)-1):
        dom = math.lcm(dom,dominator[i+1])
    return dom

def getFraction(numerator:list,dominator:list,dom):

    sumNumerator = 0

    for i in range(len(dominator)):
        sumNumerator += numerator[i] * (dom//dominator[i])
    return (sumNumerator,dom)


def DQ(num,x):
    if x == 1:
        return num
    tmp = DQ(num,x//2)
    if x % 2 == 1:
        return tmp *tmp* num % modular
    else:
        return tmp * tmp % modular


for _ in range(n):
    n, s = map(int,sys.stdin.readline().rstrip().split())
    numerator.append(s)
    dominator.append(n)

sumN, lcd = getFraction(numerator,dominator,getLcd(dominator))

total = DQ(lcd,modular-2)

print((sumN*total) % modular)

 

풀이 설명:

 

먼저 기대값을 계산하는 법에 대해서 설명을 드리겠습니다.

 

문제 예시인 n = 3, s = 7 일 경우,  7 * ( 3 ^ -1 ) mod 1000000007의 경우는 7 * ( 3 ^ 1000000005 ) mod 1000000007과 같습니다.

 

그리고 예를들어 기약 분수가 7/3 , 6/5 두개가 주어졌다면, 53/15에 대하여 연산해주면 됩니다.

표현하자면, 53 * ( 15 ^ 1000000005) mod 1000000007로요.

 

보시면 아시겠지만, 1000000005 제곱을 해주어야 하기 때문에 분할 정복을 활용하여 문제를 풀어주었습니다.

 

코드는 다음과 같이 구성하였습니다.

 

for _ in range(n):
    n, s = map(int,sys.stdin.readline().rstrip().split())
    numerator.append(s)
    dominator.append(n)

분자, 분모의 값은 입력 받아 리스트에 저장해주었습니다.

 

def getLcd(dominator:list):
    dom = dominator[0]
    for i in range(len(dominator)-1):
        dom = math.lcm(dom,dominator[i+1])
    return dom

위 코드는 분모가 될 최소 공배수를 구하는 코드입니다.

math.lcm 함수를 통해 분모들을 비교하면서 최종적인 최소공배수를 구해주었습니다.

예를 들어 3,5,7이 있다면 (3 , 5 , 7)의 최소 공배수는 3과 5의 최소 공배수인 15와 7의 최소 공배수와 동일하기 때문에 다음과 같이 구해주었습니다.

 

def getFraction(numerator:list,dominator:list,dom):

    sumNumerator = 0

    for i in range(len(dominator)):
        sumNumerator += numerator[i] * (dom//dominator[i])
    return (sumNumerator,dom)

 

그 다음은 기약분수를 구하는 코드입니다.

 

7/3 의 분자를 15로 바꾸어주려면 분자에도 5를 곱하여 35/15를 만들어 주어야 합니다.

 

따라서 구해놓은 분모들의 최소공배수를 현재 분수의 분모로 나누어 몫을 구해준 다음 

 

몫을 분자의 값과 곱해준 값을 모두 더해주었습니다.

(이미 분모들의 최소 공배수는 앞에서 구해주어서, 분자들의 값의 합만 구하면 주어진 분수들의 합을 구할 수 있습니다.)

 

그런 다음, 최종 기약 분수의 분자 분모 값을 return 해주었습니다.

 

def DQ(num,x):
    if x == 1:
        return num
    tmp = DQ(num,x//2)
    if x % 2 == 1:
        return tmp *tmp* num % modular
    else:
        return tmp * tmp % modular

다음은 분할정복을 통해 값을 구하는 코드입니다.

 

sumN, lcd = getFraction(numerator,dominator,getLcd(dominator))

total = DQ(lcd,modular-2)

print((sumN*total) % modular)

 

최종적으로 값을 출력하는 부분입니다.

분수들의 값을 모두 더한 분자 분모를 구해줍니다.( sumN, lcd )

 

원하는 값은 lcd * (sumN ^  1000000005) mod  1000000007 이므로,

 

sumN ^  1000000005 mod  1000000007 값을 분할 정복으로 구해줍니다.

 

위에서 구한 값을 lcd 와 곱한 후  1000000007 로 나눈 나머지를 구해 출력해주면 됩니다.

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

 

11779번: 최소비용 구하기 2

첫째 줄에 도시의 개수 n(1≤n≤1,000)이 주어지고 둘째 줄에는 버스의 개수 m(1≤m≤100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스

www.acmicpc.net

 

문제: 도시 간 이동하는 버스 정보와 그 비용이 주어졌을 때, 출발 도시에서 도착 도시까지 드는 최소 비용 / 최소 비용을 갖는 경로에 있는 도시의 갯수 / 최소 비용을 갖는 경로로 방문하는 도시를 순서대로  출력하는 문제입니다.

 

풀이 방법: 다잌스트라 알고리즘

 

풀이 코드:

import sys
import heapq

n = int(input())
m = int(input())

graph = [[] for _ in range(n+1)]

for _ in range(m):
    startNode, endNode, cost = map(int,sys.stdin.readline().rstrip().split())

    graph[startNode].append((cost,endNode))

start, end = map(int,sys.stdin.readline().rstrip().split())

q = [(0,start)]
route = [[] for _ in range(n+1)]
distance = [1E9] * (n+1)
distance[start] = 0

while q:
    cost, node = heapq.heappop(q)

    if cost > distance[node]:
        continue
    for newCost, newNode in graph[node]:
        dist = newCost + distance[node]
        if dist < distance[newNode]:
            distance[newNode] = dist
            heapq.heappush(q,(distance[newNode],newNode))  # 위 코드 까지는 다잌스트라
            route[newNode] = [node] # 방문하는 경로 저장

pos = end
minRoute = [end]
while pos != start:
    minRoute.append(route[pos][0])
    pos = route[pos][0]

print(distance[end])
print(len(minRoute))
for i in minRoute[::-1]:
    print(i,end =' ')

 

풀이 설명:

for _ in range(m):
    startNode, endNode, cost = map(int,sys.stdin.readline().rstrip().split())

    graph[startNode].append((cost,endNode))

시작점과 도착점 정보로, 단방향성 정보가 주어집니다. 따라서 다음과 같이 그래프에 저장해주었습니다.

while q:
    cost, node = heapq.heappop(q)

    if cost > distance[node]:
        continue
    for newCost, newNode in graph[node]:
        dist = newCost + distance[node]
        if dist < distance[newNode]:
            distance[newNode] = dist
            heapq.heappush(q,(distance[newNode],newNode))  # 위 코드 까지는 다잌스트라
            route[newNode] = [node] # 방문하는 경로 저장

이제 코드에서 핵심되는 부분은 다잌스트라 알고리즘을 통해 최소 비용 및 방문하는 도시를 탐색하는 부분입니다. 

 

여기서 기존 다잌스트라 알고리즘과 다른 부분은 경로를 저장해주는 부분입니다.

route[newNode] = [node] # 방문하는 경로 저장

위 코드를 통해 최소 비용의 거리가 갱신되었을 경우 , 경로를 저장하도록 하였는데요. 

값을 append로 덧붙여 주는 것이 아닌 갱신 되었을 때 해당 값만 저장하도록 하여 최소 비용에 들어가는 도시만을 저장해였습니다.

 

예를 들어, 출발점과 도착점을  1 -> 2, 3 -> 4 가 있다고 하였을 때,

route[ 2 ] = [ 1 ] , route[ 4 ] = [ 3 ] 과 같은 형태로 저장되게 됩니다.

도착점에 대해 시작점의 정보를 value로 가질 수 있게 저장한 것입니다.

 

pos = end
minRoute = [end]
while pos != start:
    minRoute.append(route[pos][0])
    pos = route[pos][0]

저장한 경로를 바탕으로 위 코드를 통해 최소비용을 갖는 루트의 각 도시 정보를 구했습니다.

 

최소 비용을 갖는 루트가 1 -> 4 - > 5 라고 하였을 때,

route[ 5 ] =  [ 4 ]

route[ 4 ] = [ 1 ]  와 같이 저장이 되게 됩니다.

 

이를 도착점 부터 탐색하여 도착점 5를 통해 중간 도시인 4를, 중간 도시 4를 통해 시작 도시 1을 배열에 저장해줍니다.

 

print(distance[end])
print(len(minRoute))
for i in minRoute[::-1]:
    print(i,end =' ')

마지막으로 각 정보를 출력해주는데, 거쳐간 도시의 정보는 역순으로 저장해주었으므로 역순으로 출력해주었습니다.

+ Recent posts