본문 바로가기
프로그래밍/Algorithm

구간의 합 구하기

by 타미타레 2024. 4. 24.

목차

    1. 풀이과정

    1) 문제

    백준 11659번 구간합구하기 : https://www.acmicpc.net/problem/11659

     

    11659번: 구간 합 구하기 4

    첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다. 셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j

    www.acmicpc.net

     

    2) 풀이

    시간 초과 로직

    import java.util.Scanner;
    
    public class 구간합구하기_시간초과 {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            int N = sc.nextInt();
            int M = sc.nextInt();
            int[] numbers = new int[N];
    
            for(int i = 0; i < N; i++) {
                numbers[i] = sc.nextInt();
            }
    
            for(int k = 0; k < M; k++) {
                int i = sc.nextInt()-1;
                int j = sc.nextInt();
    
                int sum = 0;
                for(int index = i; index < j; index++) {
                    sum += numbers[index];
                }
                System.out.println(sum);
            }
            sc.close();
        }
    }

    이 로직내에 이중 for문이 있기때문에 시간복잡도는 O(n2)이다. 문제는 11659번의  제한을 보면 

    • 1 ≤ N ≤ 100,000
    • 1 ≤ M ≤ 100,000
    • 1 ≤ i ≤ j ≤ N

    과 같은데 최악의 경우 100,000 * 100,000 = 100억, 즉 100억의 연산을 해야한다. 1억번의 연산이 일반적으로 1초의 시간이 걸린다고 했을 때 100억의 연산은 100초가 걸리는 셈이다. 그리고 11659번 문제의 시간 제한은 1초 이기에 이 로직은 적합한 알고리즘이 아니다. 그렇다면 이 문제에 적합한 알고리즘은 무엇일까? 그 답은 구간 합 알고리즘에 있다.

     

    구간 합 

    • 구간 합은 합 배열을 이용하여 시간 복잡도를 더 줄이기 위해 사용하는 특수한 목적의 알고리즘이다.
    • 구간 합을 구할려면 합 배열을 먼저 구해야 한다.
      • 배열 A와 합 배열 S 있을 때, 합 배열 S 정의 
      • S[i] = A[0] + A[1] + A[2] + ... + A[i-1] + A[i] // A[0]부터 A[i]까지의 합
    • 합 배열을 구할 경우 일정 범위의 합을 구하는 시간 복잡도는 O(N)에서 O(1)로 감소한다.
      • 합 배열 S를 만드는 공식 -> S[i] = S[i-1] + A[i]
      • i에서 j까지 구간 합을 구하는 공식 -> S[j] - S[i-1]

    예시)

     

    - 위 예시에서 2~4 구간합을 구할려면 S[j] - S[i-1] 공식을 활용한다.

    - S[4] - S[1] 이 되므로 14 - 5가 되고 정답은 9가 된다. 

     

    2. 정답

    정답 - 1, Scanner 사용

    import java.util.Scanner;
    
    public class 구간합구하기_1 {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int N = sc.nextInt();
            int M = sc.nextInt();
            int[] sums = new int[N+1];
            int sum = 0;
    
            for (int i = 0; i < N; i++) {
                sum += sc.nextInt();
                sums[i+1] = sum;
            }
    
            for (int k = 0; k < M; k++) {
                int i = sc.nextInt();
                int j = sc.nextInt();
    
                System.out.println(sums[j] - sums[i-1]);
            }
            
            sc.close();
        }
    }

     

    정답 - 2, BufferedReader, StringTokenizer 사용 

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.StringTokenizer;
    
    public class P11659_구간합구하기 {
        public static void main(String[] args) throws IOException {
            BufferedReader bufferedReader = 
            	new BufferedReader(new InputStreamReader(System.in));
            StringTokenizer stringTokenizer = new StringTokenizer(bufferedReader.readLine());
    
            int suNo = Integer.parseInt(stringTokenizer.nextToken());
            int quizNo = Integer.parseInt(stringTokenizer.nextToken());
            long[] S = new long[suNo + 1];
    
            stringTokenizer = new StringTokenizer(bufferedReader.readLine());
            for(int i = 1; i <= suNo; i++) {
                S[i] = S[i - 1] + Integer.parseInt(stringTokenizer.nextToken());
            }
    
            for(int k = 0; k < quizNo; k++) {
                stringTokenizer = new StringTokenizer(bufferedReader.readLine());
                int i = Integer.parseInt(stringTokenizer.nextToken());
                int j = Integer.parseInt(stringTokenizer.nextToken());
                System.out.println(S[j] - S[i-1]);
            }
    
        }
    }

    BufferedReader와 StringTokenizer사용시 Scanner 사용할 때 보다 로직이 복잡해지지만 Scanner보다 연산이 빨라 연산이 많은 알고리즘에서 많이 쓰이는 클래스이다. 

    위 이미지를 보면 아래가 BufferedReader를 사용했을 때, 위가 Scaaner를 사용했을 때 제출결과이다. 사용되는 메모리와 연산 속도가 BufferedReader가 Scanner보다 월등히 나을 걸 알 수 있다.

    '프로그래밍 > Algorithm' 카테고리의 다른 글

    [투 포인터] 주몽의 명령  (0) 2024.04.29
    [투 포인터] 연속된 자연수의 합 구하기  (0) 2024.04.27
    배열과 리스트 실전문제  (0) 2024.04.23
    배열과 리스트  (0) 2024.04.22
    알고리즘  (0) 2024.04.18

    댓글