역시 Dynamic Programming 문제는 점화식을 잘 세우는게 정말 중요하다.

이거에 따라 풀 수 있는 문제인지, 절대 못 푸는 문제인지 정해지니까..

그리고 무의식적으로 Java로 코테를 준비할 때는 속도를 위해 모든 필드와 메서드를 static으로 선언해서 사용해왔다.

보다 객체지향적으로 짜려고 데이터 입력받는 책임을 수행하는 Reader 클래스를 따로 구현했는데(FastCampus 강의를 수강하며 참고함), 내부 클래스에도 static을 꼭 선언해야 하나 해서 읽던 Effective Java를 다시 폈다.

내부 클래스를 구현할 때는 내부 클래스가 외부 클래스에게 위임하거나 호출하는게 없다면, static으로 구현하라고 나와있다.

가장 큰 이유는, static으로 구현하지 않는다면 외부 클래스로 부터 내부 클래스의 인스턴스가 외부 참조가 생기기 때문에, 추가적인 메모리 사용하기도 하며 더 나아가 GC(Garbage Collector)가 참조 메모리를 찾지 못해서 해당 메모리를 처리할 수 없다는 것이다.

static으로 사용하지 않는 경우는 다양하지만, 이는 적어도 코테에서는 크게 신경쓸 필요는 없는 것 같다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/* BOJ no.2156 - 포도주와 시식 (DP) */
public class Main {

    static int N;
    static int[] grapeJuiceAmounts;
    static int[] amountsNotDrinkLast;
    static int[] amountsDrinkLast;
    static int amountMaximumDrink;
    
    public static void main(String[] args) {
        input();
        calculateMaximumDrink();
        printResult();
    }

    static void printResult() {
        System.out.println(amountMaximumDrink);
    }

    static void calculateMaximumDrink() {
        int size = N + 1;
        for (int idx = 3; idx < size; idx++) {
            amountsNotDrinkLast[idx] = Math.max(amountsNotDrinkLast[idx - 1], amountsDrinkLast[idx - 1]);
            amountsDrinkLast[idx] = Math.max(amountsNotDrinkLast[idx - 2] + grapeJuiceAmounts[idx - 1] + grapeJuiceAmounts[idx]
                                                , amountsDrinkLast[idx - 2] + grapeJuiceAmounts[idx]);
        }
        
        amountMaximumDrink = Math.max(amountsNotDrinkLast[N], amountsDrinkLast[N]);
    }

    static void input() {
        Reader reader = new Reader();
        N = reader.readInt();
        int size = N + 1;
        grapeJuiceAmounts = new int[size];
        amountsNotDrinkLast = new int[size];
        amountsDrinkLast = new int[size];
        
        for (int idx = 1; idx < size; idx++) {
            grapeJuiceAmounts[idx] = reader.readInt();
        }
        
        amountsNotDrinkLast[1] = 0;
        amountsDrinkLast[1] = grapeJuiceAmounts[1];
        if (N > 1) {
            amountsNotDrinkLast[2] = grapeJuiceAmounts[1];
            amountsDrinkLast[2] = grapeJuiceAmounts[1] + grapeJuiceAmounts[2];
        }
    }

    static class Reader {
        BufferedReader bufferedReader;
        StringTokenizer stringTokenizer;
        
        Reader() {
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        }

        String read() {
            while (stringTokenizer == null || !stringTokenizer.hasMoreElements()) {
                try {
                    stringTokenizer = new StringTokenizer(bufferedReader.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return stringTokenizer.nextToken();
        }

        int readInt() {
            return Integer.parseInt(read());
        }        
    }
}