https://flutter-ko.dev/docs/development/data-and-backend/json

 

JSON과 직렬화

어느 시점부터 웹 서버와 통신하지 않거나 구조화된 데이터를 적절하게 보관하지 않는 모바일 앱을생각하기 어려워졌습니다. 네트워크와 연결된 앱을 제작할 때, 결국에는 제법 괜찮은 JSON을사

flutter-ko.dev

플러터를 사용하면서, 제 아무리 'flutter pub run build_runner build' 코드를 돌려도 '.g.dart' 파일이 생성이 되지 않아 너무 고생했었다.. 검색해도 잘 나오지 않은 실수라서 혹시나 같은 실수로 인해 고생할 사람을 위해 글을 남겨보려고 한다.

 

 

일단 먼저 플러터 개발자 사이트에 직렬화에 대한 설명히 자세히 나와있다.

 

다음 내용을 간략히 요약하자면 다음과 같다.

먼저, pubspec.yaml 폴더에 다음과 같이 추가해주고 'flutter pub get'을 실행해준다.

 

dependencies:
     json_annotation: ^4.6.0
dev_dependencies:
     build_runner: ^2.1.7
     json_serializable: ^6.1.4
 
 
그런 다음 다음과 같이 코드를 구성해주면 된다.
 

 

1.  'json_annotation.dart' 패키지를 import 해준다.

 

2. part '파일 명.g.dart'를 해준다.

 

3. map 구조에서 새로운 객체(코드에서는 Example)를 생성하기 위한 생성자인 User.fromJson() 생성자,  객체를 map 구조로 변환하기 위한 메서드인 toJson() 메서드를 담은 class를 다음과 같이 만들어 준다.

 

4. Terminal에서 'flutter pub run build_runner build' 를 실행하여 준다.

 

5. 생성된 '.g.dart'를 확인 할 수 있다.

 

근데 여기서 주의해야할 점이 있다.

이 부분을이 충족 되지 않으면 '.g.dart' 파일이 생성되지 않으므로 꼭!! 신경써줘야 하는 부분이다.

 

1. part 'OOOO.g.dart' 안에 들어가는 파일 명  부분(OOOO에 해당되는 부분)은 해당 파일 명과 동일해야한다.

 

2. FromJson() 생성자와  ToJson() 메소드 앞에 붙는 이름은 해당 Class 명과 동일해야한다.

(특히 이 부분에서, '_$'  또한 빠지면 안된다. 위 코드와 같이   _ExampleToJson() 과 같이 온전하게 작성해야한다)

 

3. 마지막으로, 제가 제일 고생했던 부분인데 코드가 저장되지 않은 상태로는 생성되지 않습니다.

코드를 꼭 저장하고 명령어를 실행해주세요!

 

잘못된 방식으로 코드를 빌드할 경우 다음과 같은 결과를 얻을 수 있다.

flutter pub run build_runner build과정에서 아무런 action이 완료되지 않는 모습
flutter pub run build_runner build 결과 output이 나오지 않는 경우

 

 

그럼 올바르게 코드를 작성한 후, 결과를 확인해보면 다음과 같다.

 

flutter pub run build_runner build결과, 깔끔하게 output이 나오는 것을 볼 수 있다.

터미널에서 위와 같은 결과를 얻고 나면, 왼쪽 원본파일 밑에 다음과 같은 파일이 생성된 것을 볼 수 있다.

해당 파일의 내용은 다음과 같다.

위와 같이 자동적으로 생성된 코드를 만날 수 있다.

 

 

주의해야할 점 3번 때문에 정말 고생했는데 아무리 검색해도 나오지 않더라구요.

한참 고생하다가 원인을 찾게 되었습니다.

 

이 글을 보시는 분들은 꼭 위 3가지 확인해보시고 고생하지 않기를 바랍니다!

 

 

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

 

10816번: 숫자 카드 2

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,

www.acmicpc.net

문제: 주어진 숫자 카드에 대해서, 각 수가 적혀진 숫자카드가 몇장 있는지 출력하는 문제입니다.

 

풀이 방법: heapq를 사용했습니다. 

이진 탐색 문제인 것은 알았습니다.

다만, 주어진 값들을 count 하면 되는 문제이므로 heapq.heappop()이 이진 탐색과 같은 log의 시간 복잡도를 갖는다는 것을 활용하였습니다.

 

 

풀이 코드:

import heapq
import sys

MAX = 10000000 * 2 + 2

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

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

count = [0] * MAX

while lst:
    count[heapq.heappop(lst)] += 1


for i in range(m):
    print(count[lst2[i]],end = ' ')

 

 

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

 

9012번: 괄호

괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고

www.acmicpc.net

문제: 주어진 괄호로 이루어진 문자열이 올바른 괄호 문자열인지 판단하는 문제입니다.

 

풀이 방법: stack(list)을 활용하여 '(' 에 대해서 ')'가 맞게 있는지 확인하였습니다.

 

풀이 코드:

import sys

t = int(sys.stdin.readline())
for _ in range(t):
    stack = []
    ps = list(sys.stdin.readline().rstrip())
    vps = True

    for nps in ps:
        if nps =='(':
             stack.append(nps)
        else:
           if stack:
               stack.pop()
           else:
               vps = False
               break

    if vps and not stack:
        print('YES')
    else:
        print('NO')

 

풀이 설명:


for nps in ps:
    if nps =='(':
         stack.append(nps)
    else:
       if stack:
           stack.pop()
       else:
           vps = False
           break

여기가 핵심 코드인데요. 

 

입력 값을 하나씩 확인하면서 '(' 를 만날 경우, stack에 담아줍니다.

 

그러고 난 후, ')'를 만날 경우, stack에서 '(' 를 하나씩 pop 해주게 되면, '()'가 이루어집니다.

 

이때, ')' 가 '('  보다 많아서 입력받은 문자열은 남아있는데, stack이 빈 경우가 생기게 됩니다. 

혹은 '')(' 패턴이 들어간 경우에도 stack이 빈 경우가 생기게 됩니다. ex. ')('  '()  )(  ()'

if stack:
    stack.pop()
else:
    vps = False
    break

위 코드를 통해, stack이 비었는지 여부를 확인해주고 만약에 비었다면 ')' 갯수가 더 많은 것이므로 vps라는 변수에 False를 설정하고 break 해줍니다.

 

만약 반복문이 종료가 되었을 때, ')'  갯수가 적어서 '(' 이 남아있는 경우가 있으므로 이 부분도 신경써줘야 합니다.

if vps and not stack:
    print('YES')
else:
    print('NO')

 

위 코드와 같이, 구성해줬는데요.

vps가 True, 그리고 stack이 빈 경우에만 'YES'를 출력하고 이외에는 다 'NO'를 출력하도록 설정되어있습니다.

 

')' 갯수가 많거나 ')(' 패턴이 있다면, vps가 False 일 것이고 

')' 갯수가 적을 경우, stack에 '(' 이 남아있을 것이므로 'NO'가 출력되게 될 것입니다.

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

 

1446번: 지름길

첫째 줄에 지름길의 개수 N과 고속도로의 길이 D가 주어진다. N은 12 이하인 양의 정수이고, D는 10,000보다 작거나 같은 자연수이다. 다음 N개의 줄에 지름길의 시작 위치, 도착 위치, 지름길의 길이

www.acmicpc.net

 

 

문제: 고속도로 길이와 지름길이 주어졌을 때, 고속도로를 운전해야하는 최소 거리를 구하는 문제 입니다.

 

풀이 방법:  다이크스트라 알고리즘을 활용하였습니다. 알고리즘을 활용하여, 지름길 정보에 대해 계속해서 갱신해주는 방식을 사용하였습니다.

 

풀이 코드:

import sys
import heapq
MAX = 10001

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

road = [[]*(MAX) for _ in range(MAX)]
heap = []
dist = [i for i in range(MAX)]

for _ in range(n):
    start, end, cost = map(int,sys.stdin.readline().rstrip().split())
    heapq.heappush(heap,(end,start,cost))

while heap:
    newEnd, newStart, newCost = heapq.heappop(heap)

    if newCost > (dist[newEnd] - dist[newStart]) or newEnd > d:
        continue

    distance = dist[newStart] + newCost

    if dist[newEnd] > distance:
        dist[newEnd] = distance

    for i in range(newEnd+1,MAX):
        dist[i] = min(dist[i],dist[i-1]+1)

print(dist[d])

 

풀이 설명:

dist = [i for i in range(MAX)]

먼저 지름길이 없을 경우에는 목적지 x 까지 주행해야 하는 거리는 x이므로, 다음과 같이 저장해줬습니다.

for _ in range(n):
    start, end, cost = map(int,sys.stdin.readline().rstrip().split())
    heapq.heappush(heap,(end,start,cost))

지름길에 대한 정보를 heapq를 활용해 저장하였습니다. 다만 저장할 때, 출발점으로부터 가까운 지름길 정보부터 갱신해주기 위해 (end,start,cost)형태로 저장하였습니다.

*heapq.heappush를 사용하면 end 값 기준으로 오름차순 정렬하여 데이터가 저장됩니다.

 

while heap:
    newEnd, newStart, newCost = heapq.heappop(heap)

    if newCost > (dist[newEnd] - dist[newStart]) or newEnd > d:
        continue

    distance = dist[newStart] + newCost

    if dist[newEnd] > distance:
        dist[newEnd] = distance

그런 다음 저장된 값을 순차적으로 접근하면서, 지름길에 대한 정보를 업데이트 해줬습니다. 

기존에 다른 지름길을 통해 이미 end값 까지 도착하는 거리가 갱신되었을 수 있으므로, if문을 통해 현재 계산된 거리와 기존에 저장된 거리를 비교하여 최소값을 저장해주었습니다.

 

for i in range(newEnd+1,MAX):
    dist[i] = min(dist[i],dist[i-1]+1)

마지막으로, 지름길 정보가 갱신 될 때 마다 지름길에 대한 정보를 전체 거리정보가 담겨있는 dist에 업데이트 해줬습니다.

 

 

안녕하세요.

오늘은 백준 알고리즘을 풀며 만났던 TypeError: cannot unpack non-iterable int object 에 대해 글을 써보려 합니다.

 

먼저, 해당 에러를 접한 상황은 다음과 같습니다. (백준 알고리즘 1916번, 18352번)

저는 deque에 (distance,node)의 값을 넣고, pop() 할 때마다 두 값을 한번에 얻고 싶었습니다. 다음과 같이 말이죠.

q = deque((distance,node))
newDistance , newNode = q.pop()

하지만, TypeError가 발생하여서 문제는 풀어야해서 항상 다음과 같이 풀었습니다.

q = deque()
q.append((distance,node))
newDistance , newNode = q.pop()
print(newDistance,newNode)

 

문제를 풀고 나서, 왜 그렇지 하고 여러가지 테스트를 해보았는데 역시나.. deque를 잘못사용하고 있었습니다.

테스트를 위해 아래와 같이 코드를 돌려보았습니다.

q = deque((0,99))
print(q)
q = deque([0,99])
print(q)
q = deque([(0,99)])
print(q)

 

그랬더니 위와 같은 결과 값이 나왔습니다. 여기서 아뿔사 했습니다..(역시 기본이 제일 중요하다는 것을 뼈저리게 느끼며...)

 

파이썬의 deque() 함수는 선언할 때 들어왔던 인자를 자동으로 deque화 시켜서 저장한다는 것을 놓쳤던 것입니다.

 

다음과 같이 사용하면, q[0]에 0이 q[1]에 99가 들어가는 것이죠.

print를 통해 출력해보면 이런 결과값이 나옵니다.

q = deque((0,99))
print(q[0],q[1])

q = deque([0,99])
print(q[0],q[1])

 

 

 

q = deque()
q.append((distance,node))
newDistance , newNode = q.pop()

***

(3번째줄) 이렇게 사용한다면, 입력받고자 하는 변수 갯수와 입력하고자 하는 변수 갯수가 맞지 않기 때문에 Error가 발생하는 것이었습니다.

 

'TypeError: cannot unpack non-iterable int object' -> ' iterable 하지 않은 int형 객체를 unpack 할 수 없다' Error 내용은

입력하고하 하는 변수 갯수는 1개인데, 입력받고자 하는 변수 갯수가 2개 여서 값을 할당할 수 없다라는 것입니다.

 

 

그렇다면 제가 의도한 방식대로 하려면 어떻게 했어야 할까요?

 

정답은 다음과 같이 [] 안에 다시 tuple형태로 감싸서 값을 담는 것입니다.

q = deque([(0,99)])

한번 값을 확인해보면 다음과 같이 나옴을 알 수 있습니다.

q = deque([(0,99)])

print(q)

distance , node = q.pop()
print('distance:',distance,'node:',node)

 

 

오늘은 deque를 사용하며, 발생했던 TypeError에 대해서 정리해보았습니다.

진짜 오늘도 기본이 중요함을 뼈저리게 깨달으면서 포스팅을 마치겠습니다.

읽어주셔서 감사합니다.

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

 

18352번: 특정 거리의 도시 찾기

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ N ≤ 300,000, 1 ≤ M ≤ 1,000,000, 1 ≤ K ≤ 300,000, 1 ≤ X ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개

www.acmicpc.net

어제 1916번 최소비용 구하기 문제를 풀며, 아직 다익스트라 알고리즘에 대해 이해가 부족하다고 느껴 해당 문제를 찾아서 풀어봤습니다.

 

문제: 도시의 개수, 도로의 개수, 찾고자 하는 거리 정보, 출발 도시의 번호, 그리고 도로의 정보가 주어졌을 때, 거리가 K인 도시를 찾는 문제입니다.

 

풀이 방법: 다익스트라 알고리즘을 활용했습니다.

 

풀이 코드:

 

import sys
import heapq


n,m,k,x = map(int, sys.stdin.readline().rstrip().split())
graph = [[] *(n+1) for _ in range(n+1)]

for _ in range(m):
    node1, node2 = map(int,sys.stdin.readline().rstrip().split())

    graph[node1].append(node2)

q = [(0,x)]

dist = [1E9] * (n+1)
dist[x] = 0

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

    if dist[node] < cost: continue

    for i in graph[node]:
        newCost, newNode = 1, i

        distance = newCost + cost
        if dist[newNode] > distance:
            dist[newNode] = distance
            heapq.heappush(q,(distance,newNode))
        else:
            continue

if dist.count(k) == 0:
    print(-1)
else:
    for j in range(1,n+1):
        if dist[j] == k:
            print(j)

풀이 설명:

graph에는 도시 ~ 도시 까지 도로 정보를 담습니다. 

for _ in range(m):
    node1, node2 = map(int,sys.stdin.readline().rstrip().split())

    graph[node1].append(node2)

graph에는 도시 ~ 도시 까지 도로 정보를 담습니다. 

 

q = [(0,x)]

dist = [1E9] * (n+1)
dist[x] = 0

그리고 heapq에는 (거리, 도시)의 값이 들어 갑니다. 초기 값은 (0, 시작 도시)로 설정합니다.

dist는 거리를 계산하여 담을 배열로 시작점 ~ 시작점의 거리는 0이므로, 0으로 설정해줍니다.

 

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

    if dist[node] < cost: continue

    for i in graph[node]:
        newCost, newNode = 1, i

        distance = newCost + cost
        if dist[newNode] > distance:
            dist[newNode] = distance
            heapq.heappush(q,(distance,newNode))
        else:
            continue

while문을 통해서 갈 수 있는 도시들의 정보를 꺼냅니다.

 

다음 heapq를 통해 얻은 정보를 통해 for문으로 graph에서 연결된 도시 정보를 끌어옵니다.

 

마지막으로 연결된 도시의 거리정보를 계산한 다음 계속해서 거리정보 값들 중 작은 값으로 거리 값을 갱신해줍니다.(dist 배열)

 

 

시작점 = 1, 도로 정보 (1,2) (2,3) (3,4) 가 주어졌다고 가정해보겠습니다. 

위 예시를 통해 자세히 설명하자면, heapq에는 초기값 (0,1)가 들어갔다가 while 문 안에서 (0,1)이 pop 되겠죠.

그럼 for문을 통해 1에서 갈 수 있는 2의 값이 꺼내지고(in graph[1]), 도시 1에서 2로 가는 거리의 값인 1이 dist[2]의 값으로 계산 된 후 다시 heapq에는 (1(dist),2(node))가 들어가게 됩니다.

 

그럼 다음 싸이클에서는 newCost = 1, newNode = 2가 나오게 되고, for문을 통해 2에서 갈 수 있는 노드를 찾게되겠죠.

그럼 또 (2,3)인 도로 정보가 graph[2]에 담겨있으니, 이번에는 1->2까지의 거리 + 2->3까지의 거리가 계산된 후 (2(dist),3(node))

결과적으로 노드 4까지 도달하게 되면, 1에서 2,3을 거쳐 4까지 도달하는 최소 거리가 계산되게 됩니다.

 

 

 

 

if dist.count(k) == 0:
    print(-1)
else:
    for j in range(1,n+1):
        if dist[j] == k:
            print(j)

시작점으로 부터 도달할 수 있는 모든 노드의 거리 정보를 계산하고 나면, 다음 구문을 통해 요구하는 결과 값을 출력해줍니다.

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

 

1916번: 최소비용 구하기

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

www.acmicpc.net

solved.ac의 class 4로 진입했는데 갑자기 난이도가 너무 높아져서 당황했습니다. 진짜 해결 방안이 쉽사리 떠오르지 않더라구요. 

문제 푸는데 시간이 꽤 오래 걸린 문제였습니다...

 

문제: 도시 ~ 도시의 비용이 주어졌을 때, 출발 도시에서 도착 도시까지의 최소비용을 구하는 문제 입니다.

 

풀이 방법: heapq를 이용하여 최소 cost 계산.

 

풀이 설명:

일단 입력 값에 대해 다음과 같이 graph를 구성하였습니다.

for _ in range(m):
    node1, node2, value = map(int,sys.stdin.readline().rstrip().split())
    graph[node1].append((value,node2))

이때, 값을 (value,node) 순으로 구성한 이유는 heapq를 비용 기준으로 활용할 계획이기 때문입니다.

 

graph와 heapq를 통해 시작점으로 부터 갈 수 있는 노드의 비용들을 탐색하고, 이 과정에서 도착 노드에 대해 나오는 결과 값들 중 최소 값만 저장되도록 다음과 같이 코드를 작성하였습니다 . 

heapq.heappush(heap, [0, start])# 초기 값을 시작점으로 setting
dist[start] = 0# 시작점 cost = 0

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

    if cost > dist[node]:
        continue

    for i in graph[node]:# 시작점으로부터 도달할 수 있는 노드 탐색
        nextCost, nextNode = i
        distance = nextCost + cost

        if dist[nextNode] > distance:
            dist[nextNode] = distance# 기존 입력된 거리와 새로 계산된 거리의 최소 값 저장
            heapq.heappush(heap, [distance, nextNode])
        else:
            continue

풀이 코드:

import sys
import heapq

n = int(sys.stdin.readline())
m = int(sys.stdin.readline())

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

for _ in range(m):
    node1, node2, value = map(int,sys.stdin.readline().rstrip().split())
    graph[node1].append((value,node2))

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

heapq.heappush(heap, [0, start])# 초기 값을 시작점으로 setting
dist[start] = 0# 시작점 cost = 0

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

    if cost > dist[node]:
        continue

    for i in graph[node]:# 시작점으로부터 도달할 수 있는 노드 탐색
        nextCost, nextNode = i
        distance = nextCost + cost

        if dist[nextNode] > distance:
            dist[nextNode] = distance# 기존 입력된 거리와 새로 계산된 거리의 최소 값 저장
            heapq.heappush(heap, [distance, nextNode])
        else:
            continue
print(dist[destination])

 

문제를 푸시다보면 다양한 에러를 만나실텐데요. 

오늘은 백준알고리즘 1697번을 풀면서 발생한 메모리 초과에 대해서 글을 써보려고 합니다.

 

메모리 초과가 발생하는 이유를 간단히 말씀드리자면 '스택 오버플로우'가 발생하기 때문입니다.

문제를 푸시면서 가장 많이 접할 수 있는 경우는 다음 두 경우라고 생각합니다.

 

1.  너무 많은 변수들을 배열 등에 저장할 경우.

2. DFS 등에서 재귀적 호출을 통해 너무 많은 함수들을 호출할 경우.

 

저 같은 경우에는, 백준 알고리즘 1697번에서 1번에 의한 메모리 초과가 발생하였는데요.

시작점으로부터 도착점까지 도달할 때 까지, (시작점-1, 시작점+1, 시작점*2)에 해당하는 값들을 탐색하는 과정에서 실수를 했습니다.

 

시작점 = 5, 도착점 =17을 기준으로 처음 들어간 (5-1,5+1,5*2)에 대해서 다음 싸이클 값들이 어떻게 저장되는지 출력한 것인데요.

아래 스크린샷을 보시면, 하나의 경우의 수에 대하여 계속해서 3가지의 새로운 경우의 수가 들어가고 있습니다.

해당 문제의 시작점과 도착점이 될 수 있는 정수의 범위가 0~100,000 이었으니, 시작점이 1이고 도착점이 100,000일 경우 엄청나게 많은 값들이 큐에 저장되었겠죠?

 

이로 인해, 계속해서 메모리 초과가 발생하였습니다.

 

메모리 초과에 대한 해결 방안은 다음과 같습니다.

1. 배열에 너무 많은 값들이 들어가는 경우에는 값이 들어갈 수 있는 범위 제한 및 들어가는 데이터 수를 제한 하는 조건 등을 통해서 해결하실 수 있습니다.

 

1697번을 예시로 들자면,

Max = 10 ** 5
visitedCnt = [0]*(Max+1)

배열을 선언할 때, 적당한 크기로 배열의 사이즈를 할당합니다.

 

for i in pos:
    if 0 <= i <= Max and not visitedCnt[i]:
        visitedCnt[i] = visitedCnt[idx] + 1
        queue.append([i,visitedCnt[i]])

두번째로는 들어갈 수 있는 값들의 범위를 지정하여 무한 루프를 방지해주고, 해당 값을 처리한 적이 있는지 확인하여 중복된 데이터가 처리되지 않도록 하였습니다.

 

위, 두가지 방법을 통해 메모리 초과를 해결 할 수 있었습니다.

 

2. 재귀 등으로 인한 너무 많은 함수 호출로 인한 메모리 초과의 경우에는 다음과 같습니다.

첫번째로는 재귀가 너무 많은 횟수가 반복되지 않도록 적절한 조건 설정 해주어 메모리 초과가 발생하지 않도록 하는 방법이 있고,

두번째로는 재귀함수를 반복문으로 푸는 것입니다.

재귀함수를 통해 호출된 함수는 메모리 영역의 스택에 저장되는데, 동일한 함수여도 호출 될 때마다 스택영역에 계속해서 메모리가 할당되게 됩니다. 너무 많은 함수가 호출이 된다면 스택 영역의 메모리를 초과하게 되는 '스택 오버플로우'가 발생합니다.

 

즉, 핵심은 스택 영역의 메모리를 초과하지 않게 사용하는 것이죠.

 

첫번째 방법은 스택 영역을 활용하되 사용하는 메모리 영역을 초과하지 않게 하는 방법이고, 두번째 방법은 아예 스택 영역을 사용하지 않는 방법입니다.

 

 

메모리 초과라는 문제에 대해 조금이나마 도움이 되었으면 좋겠습니다. 

읽어주셔서 감사합니다.

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

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

문제: 1초마다 해당 위치(X)에서 (X-1,X+1,X*2) 로 이동할 수 있을 때, 주어진 값(K) 까지 도달하는데 몇초가 걸리는지 구하는 문제입니다.

 

풀이 방법: BFS를 통해 문제를 해결하였습니다.

 

풀이 설명:

그래프 상하좌우 탐색에서 영감을 받아 해당 위치에서 이동할 수 있는 방향을 지정한 후(pos배열), 해당 점들을 방문하여 횟수를 저장하였습니다.

이때, 해당 값들에 접근하는 방식을 BFS를 통해 풀어냈습니다.

 

pos = [idx-1,idx+1,idx*2]

또한, queue에서 popleft()를 통해 값을 꺼내줌으로써 매 초마다 접근할 수 있는 점들을 순차적으로 탐색할 수 있게 설정하였습니다.

idx,cnt = queue.popleft()

풀이 코드:

import sys
from collections import deque

Max = 10 ** 5

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

visitedCnt = [0]*(Max+1)

def bfs():
    queue = deque()
    queue.append([n,0])

    while queue:
        idx,cnt = queue.popleft()
        if(idx == k):
            print(cnt)
            break

        pos = [idx-1,idx+1,idx*2]

        for i in pos:
            if 0 <= i <= Max and not visitedCnt[i]:
                visitedCnt[i] = visitedCnt[idx] + 1
                queue.append([i,visitedCnt[i]])
bfs()

 

틀렸던 코드:

import sys
from collections import deque

Max = 10 ** 5

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

visitedCnt = [0]*(Max)

def bfs():
    queue = deque()
    queue.append([n,0])

    while queue:
        idx,cnt = queue.popleft()
        if(idx == k):
            print(cnt)
            break

        pos = [idx-1,idx+1,idx*2]

        for i in pos:
            if 0<= i <= Max:
                visitedCnt[i] = cnt + 1
                queue.append([i,visitedCnt[i]])

bfs()

첫번째로, 최대 입력 값이 10만 이기 때문에 Max 변수에 해당 값을 저장해주었습니다.

근데, 배열 선언하는 부분에서 기초적인 실수를 했습니다. bfs함수에서 배열에 접근할 때, value = 5 라면 배열 index = 4 인 부분에 값을 저장하지 않고 index=5에 값을 저장하도록 코드를 구현하였습니다.

근데..

visitedCnt = [0]*(Max)

배열 크기를 Max+1 이 아닌 Max로 선언하여 IndexError가 발생하였습니다. value = Max 일 때, 에러가 발생한 것이죠.

정말 기초가 얼마나 중요한지 새삼 깨닫는 시간이었습니다..

 

또한, 방문했던 위치에 대하여 방문 여부를 확인 해주지 않아 큐에 너무 많은 값들이 담겨 '메모리 초과'가 발생하였습니다. 방문 여부 처리는 bfs의 기초라고 할 수 있는데 정답을 출력하는 것에만 매몰되어버려서 놓친 것이죠.

(즉, 매 초마다 방문할 수 있는 위치를 순차적으로 접근하기 때문에 값에 도달한 순간의 시간이 최소 값이기 때문에 방문 여부를 적용할 수 있었습니다.)

<틀렸던 코드>

for i in pos:
    if 0<= i <= Max:
        visitedCnt[i] = cnt + 1
        queue.append([i,visitedCnt[i]])

<정답 코드>

for i in pos:
    if 0 <= i <= Max and not visitedCnt[i]:
        visitedCnt[i] = visitedCnt[idx] + 1
        queue.append([i, visitedCnt[i]])

 

단순 bfs 문제라 생각해 엄청 간단하게 풀 수 있을줄 알았지만, 메모리 초과와 기초적인 실수들로 인한 IndexError로 인해 엄청나게 고생했던 문제입니다.(심지어 짜증마저 났던..)

하지만, 원인을 알고나서는 해당 원인을 미처 고려하지 못했던 제 부족함을 알 수 있게 되었습니다.

 

메모리 초과 부분은 제가 부족했던 점과 함께 별도로 좀 더 자세히 포스팅하도록 하겠습니다.

 

 

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

 

10026번: 적록색약

적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)

www.acmicpc.net

문제: 그림의 색상이 주어졌을 때, 적록색약인 사람과 적록 색약이 아닌 사람이 볼 수 있는 색상의 구역 수를 각각 구하는 문제 입니다.

(이때, 적록 색약인 사람은 빨강과 초록을 구분하지 못합니다.)

 

풀이 방법: 그래프 탐색 방법중 BFS로 문제를 풀었습니다.

 

풀이 설명:

 

문제는 총 3가지 idea에서 착안해서 풀었습니다.

 

1.

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]
for k in range(4):
    x = i + dx[k]
    y = j + dy[k]

위 방식을 통한, 각 위치의 상하좌우 요소 탐색. (그래프 탐색 뿐만 아니라 해당 코드를 통해 간단하게 상하좌우 요소를 접근할 수 있다는 점에서 매우 유용한 코드라고 생각합니다.)

 

2. BFS 를 통한 그래프 탐색

 

3.  색약인 경우, 파란색을 기준으로 색 판별

if color != 'B' and array[x][y] != 'B':
    visited[x][y] = True
    q.append([x,y])
elif color == 'B' and array[x][y] == 'B':
    visited[x][y] = True
    q.append([x,y])

풀이 코드:

import sys
from collections import deque

sys.setrecursionlimit(10000)

n = int(sys.stdin.readline())

array = [[]*n for _ in range(n)]
for i in range(n):
    array[i] = list(sys.stdin.readline().strip())

cnt = 0
visited = [[False]*n for _ in range(n)]

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]

def bfs(a,b,color):

    q = deque()
    q.append([a, b])
    visited[a][b] = True
    while q:
        i, j = q.pop()

        for k in range(4):
            x = i + dx[k]
            y = j + dy[k]

            if x < 0 or x >= n or y < 0 or y >= n or visited[x][y]:
                continue

            if color == array[x][y]:
                visited[x][y] = True
                q.append([x,y])
    return


def bfs_blind(a,b,color):

    q = deque()
    q.append([a,b])

    visited[a][b] = True
    while q:
        i,j = q.pop()
        for k in range(4):
            x = i + dx[k]
            y = j + dy[k]

            if x < 0 or x >= n or y < 0 or y >= n or visited[x][y]:
                continue

            if color != 'B' and array[x][y] != 'B':
                visited[x][y] = True
                q.append([x,y])
            elif color == 'B' and array[x][y] == 'B':
                visited[x][y] = True
                q.append([x,y])
    return



for i in range(n):
    for j in range(n):
        if not visited[i][j]:
            bfs(i,j,array[i][j])
            cnt += 1

print(cnt,end=' ')
visited = [[False]*n for _ in range(n)]
cnt = 0
for i in range(n):
    for j in range(n):
        if not visited[i][j]:
            bfs_blind(i,j,array[i][j])
            cnt += 1

print(cnt)

 

일단 BFS 탐색 코드를 2가지로 나누어 구현하였습니다.

적록 색맹이 아닌 경우, 탐색 시작점의 색과 동일한지 여부만 판단하면 되는 반면에

적록 색맹인 경우, 빨간색과 초록색을 동일한 색으로 보고 판단해야하기 때문입니다.

 

이제 BFS 함수 내부에서 상하좌우 요소를 접근하는데, 

if x < 0 or x >= n or y < 0 or y >= n or visited[x][y]:
    continue

해당 코드를 통해 index가 배열을 벗어나는지를 판별함과 동시에 해당 위치를 방문한적이 있는지 판별하였습니다.

 

마지막으로 큐와 visited 배열을 활용하여 해당 노드를 방문한적 있는지 여부를 바탕으로 구역을 탐색하였습니다.

 

다른 풀이:

import sys

sys.setrecursionlimit(10000)

n = int(sys.stdin.readline())

array = [[]*n for _ in range(n)]
for i in range(n):
    array[i] = list(sys.stdin.readline().strip())

cnt = 0
visited = [[False]*n for _ in range(n)]
def bfs(a,b,eyes,color):
    dx = [0,0,-1,1]
    dy = [-1,1,0,0]

    for i in range(4):
        x = a + dx[i]
        y = b + dy[i]
        if x < 0 or x >= n or y < 0 or y >=n or visited[x][y]:
            continue

        if(eyes == 'color-blindness'):
            if((color != 'B') and array[x][y] != 'B'):
                visited[x][y] = True
                bfs(x, y, eyes, color)
            elif(color == 'B' and array[x][y] =='B'):
                visited[x][y] = True
                bfs(x, y, eyes, color)
            else:
                continue


        else:
            if color == array[x][y]:
                visited[x][y] = True
                bfs(x,y,eyes,color)

    return

for i in range(n):
    for j in range(n):
        if not visited[i][j]:
            bfs(i,j,'!color-blindness',array[i][j])
            cnt += 1

print(cnt,end=' ')
visited= [[False]*n for _ in range(n)]
cnt = 0
for i in range(n):
    for j in range(n):
        if not visited[i][j] :
            bfs(i,j,'color-blindness',array[i][j])
            cnt += 1
print(cnt)

이 코드를 통해 문제를 맞췄지만 문제를 다시 푼 이유는 다음과 같았습니다.

1. 그래프 탐색을 반복문이 아닌 재귀로 문제를 품으로써 메모리 사용량과 속도가 반복문에 비해 떨어질 것이라고 생각했습니다.

2. 함수를 호출 할 때마다, 색약의 여부에 따라 함수를 다르게 구현한 풀이 코드와 달리 색약을 판단하는 조건문이 계속적으로 동작하기 때문에 속도가 뒤떨어질 것이라고 판단했습니다.

 

첫번째가 풀이 코드인데요. 반복문으로 구현한 만큼 메모리는 적게 사용했지만 오히려 속도 부분에서는 느렸습니다.

반복적인 조건문의 동작과 재귀함수의 사용으로 인해 속도 부분은 확실하게 빠를 것이라 판단했지만 그렇지 않은 결과가 나와서 조금은 당황스러웠습니다.

 

해당 부분을 조금 더 공부한 후, 메모리와 속도 모두 효율적인 코드를 짤 수 있기 위해 노력하려고 합니다.

 

 

+ Recent posts