ABOUT ME

Today
Yesterday
Total
  • [백준] 2015. 수들의 합 4 - Python3
    카테고리 없음 2023. 2. 13. 23:18

    문제 설명

    N, K가 주어지고, N개의 수들로 이루어진 배열이 주어질 때, 부분합이 K인 경우의 수를 구하는 문제

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

     

    2015번: 수들의 합 4

    첫째 줄에 정수 N과 K가 주어진다. (1 ≤ N ≤ 200,000, |K| ≤ 2,000,000,000) N과 K 사이에는 빈칸이 하나 있다. 둘째 줄에는 배열 A를 이루는 N개의 정수가 빈 칸을 사이에 두고 A[1], A[2], ..., A[N]의 순서로

    www.acmicpc.net

    • 알고리즘 분류 - 자료 구조, 누적 합, 해시를 사용한 집합과 맵, 트리를 사용한 집합과 맵
    • 난이도 - 골드 4

    관련 개념

    부분합

    i~j 번째 요소의 합은 j번째 요소까지의 총합 - i번째 요소까지의 총합과 같다.


    접근 방법

    1. 이중반복문 (실패 - 시간 초과)

    배열의 i번째 요소까지의 합을 저장하는 배열 S를 만들고,

    0~n-1 범위의 i와 i+1~n-1 범위의 j에 대해 S[i] == k인 경우와 S[j] - S[i] == k인 경우를 카운팅 했다.

     

    2. 딕셔너리

    (다른 분들의 풀이 참고)

    부분합을 한 번 더 응용하여, 새로운 딕셔너리에 각 부분합이 등장하는 횟수를 저장함으로써 1번 풀이에서의 j에 대한 반복문을 작성하지 않고 구할 수 있다.

    먼저, j를 i로 대체하기 위해 둘을 바꿔 생각한다. i >= j 일 때, S[i] - S[j] == k를 이항하여, S[j] == S[i] - k 식을 활용함으로써 S[j]를 i와 k에 대한 식으로 대체할 수 있다.

     

    1) 0~i번째 요소의 부분합이 k인 경우

    1번 풀이와 마찬가지로 단순히 S[i] == k인 경우를 answer에 더한다.

     

    2) i~j번째 요소의 부분합이 k인 경우

    S[j] = S[i] - k라는 점을 이용하여, 딕셔너리의 키값으로 S[i] - k가 존재하는지 확인한다.

    즉, 현재는 i번째 요소를 탐색하는 시점이므로, 이전에 S[i] - S[j] == k를 만족하는 S[j] 값이 있었는지 체크하는 것이다.

    만약 있었다면 등장했던 횟수만큼을 answer에 더하고, 없었다면 더하지 않는다(0을 더한다).


    답안 코드

    1. 이중반복문 (시간 초과)

    시간 초과
    import sys
    input = sys.stdin.readline
    
    answer = 0
    n, k = map(int, input().split())
    A = list(map(int, input().split()))
    
    # S: 0~i번째 요소까지의 부분합 저장
    S = [0 for _ in range(n)]
    S[0] = A[0]
    for i in range(1, n):
        S[i] = S[i-1] + A[i] 
    
    for i in range(n):
    	if S[i] == k: # 인덱스 0~i 범위의 합이 k인 경우.
        	answer += 1
        for j in range(i+1,n): # 인덱스 i~j 범위의 합의 k인 경우.
             if S[j]-S[i] == k:
                answer += 1
                
    print(answer)

     

    2. 딕셔너리

    메모리: 47516 KB , 시간: 192 ms, 코드 길이: 524 B
    import sys
    input = sys.stdin.readline
    
    answer = 0
    n, k = map(int, input().split())
    A = list(map(int, input().split()))
    
    S = [0 for _ in range(n)]
    S[0] = A[0]
    for i in range(1, n):
        S[i] = S[i-1] + A[i] 
    
    cnt = {}
    for i in range(n):
        # 인덱스 0~i 범위의 합이 k인 경우
        if S[i] == k:
            answer += 1
        
        # 인덱스 i~j 범위의 합이 k인 경우. S[j] = S[i]-k
        answer += cnt.get(S[i]-k,0)
        
        # 인덱스 i까지 탐색했을 시점의 부분합 카운팅
        if S[i] in cnt:
            cnt[S[i]] += 1
        else:
            cnt[S[i]] = 1
                
    print(answer)

    어떻게 이런 생각을 하지..? 코드 보고 이해하는 것만으로도 한참 걸렸다. 난 똥멍청이다......ㅠㅠ

    댓글

Designed by Tistory.