문제 링크

분류

다이나믹 프로그래밍

 

문제 설명

1 × N 크기의 긴 밭에 벼가 심어져 있다. 준희는 이 벼를 수확 하려고 한다. 그런데 가운데 있는 벼를 수확을 하려면 끝에서 가운데까지 헤집고 들어가야 하므로 양 끝에 있는 벼만 수확을 할 수 있다. 처음에는 첫 번째와 N번째 벼를 수확을 할 수 있을 것이며 만약에 첫 번째 벼를 수확을 하였다면 두 번째 벼와 N번째 벼를 수확을 할 수 있다.

수확을 하였을 때 얻을 수 있는 이익은 다음과 같다. 만약에 그 벼의 가치가 v(i)라고 하고 그 벼를 k번째로 수확을 한다고 하면 v(i) × k 만큼의 이익을 얻게 된다.

만약에 벼의 가치가 차례로 1 3 1 5 2 라고 하고 첫 번째, 다섯 번째, 두 번째, 세 번째, 네 번째의 순서대로 수확을 한다고 하면 1×1 + 2×2 + 3×3 + 4×1 + 5×5 = 43 이 되어 43 만큼의 이익을 얻게 된다. 그리고 이 값이 저 벼로 얻을 수 있는 최대 이익이 된다.

우리가 구해야 할 값은 다음과 같다. 벼의 개수 N과 벼의 가치가 주어져 있을 때, 얻을 수 있는 최대 이익을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 벼의 개수 N(1 ≤ N ≤ 2,000)이 주어지고 두 번째 줄부터 N+1번쨰 줄까지 벼의 가치 v(i) (1 ≤ v(i) ≤ 1,000) 가 주어진다.

 

출력

첫째 줄에 얻을 수 있는 최대 이익을 출력한다.

 

실패한 풀이 (원인: 세밀하게 관리하지 못 한 DP 테이블)

시간 복잡도 O(n) 공간 복잡도 O(n)

아이디어: dp[n] 을 n번째 벼까지 수확 했을 때의 최대 값을 저장하는 공간으로 저장한다, dp[0] = 0 이다.

맨 처음과 맨 마지막 중에서 더 작은 값을 선택해서 수확한다. 만약 두 개의 값이 동일 하다면 작은 값이 더 가까운 쪽으로 수확한다.

모든 경우를 체크하지 못 하여 더 세밀한 DP 테이블 관리가 필요했다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    static boolean judge(int[] array,int start, int end){
        while(start<=end&&array[start]==array[end]){
            if(array[start]>array[end])
                return true;
            else if(array[end]>start)
                return false;
            start++; end--;
        }
        return true;
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[] dp = new long[n+1];
        int[] array = new int[n+1];
        // 가치: array[i]*i

        for (int i = 1; i <= n; i++) {
            array[i] = Integer.parseInt(br.readLine());
        }
        boolean way = true;

        int count = 1;
        int start = 1; int end = n;
        while(start<=end){
            if(array[start]==array[end]){
                way = judge(array,start+1,end-1);
                if(way)
                    dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[end--] *count);
                else
                    dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[start++] *count);
            }
           else if(array[start]>array[end])
               dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[end--] *count);
           else
               dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[start++] *count);
           count++;
        }
        System.out.println(dp[n]);
    }
}

성공한 풀이 (동적 프로그래밍)

시간 복잡도 O(n^2) 공간 복잡도 O(n^2)

<아이디어>

1 단계: DP 테이블 설정

  • dp[i][j]는 제일 왼쪽 끝에서 i번 칸 만큼 수확하고 제일 오른쪽 끝에서 j번 칸 만큼 수확하여 얻은 작물의 최대 가치
  • i+j <= n 인 경우에만 성립 그 외의 공간은 관리 x (아래는 사진 예시)

2 단계: 초기 테이블 세팅

3 단계: DP 테이블을 갱신하는 규칙을 찾아 로직을 구현

제일 위에 예시와 마찬가지로 벼에 대한 가치의 배열이 (인덱스 1~n) {1, 2, 3, 4, 5) 일 때

왼쪽으로 2번, 오른쪽으로 1번 수확한 상황에서 추가로 4번째 작물을 수확해야 하는 경우 갱신 방법

  • 왼쪽의 벼를 추가로 수확 하는 경우
    • dp[3][1] = dp[2][1]+(3 + 1) *array[3];
  • 오른쪽의 벼를 추가로 수확 하는 경우
    • dp[2][2] = dp[2][1]+(2+2)*array[n-2+1];

4 단계: 목표하고자 하는 내용을 출력

모든 벼를 수확했을 때 최대값을 출력해야 하므로 i+j == n 인 경우의 값 중 최고값을 출력

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[][] dp = new long[n+1][n+1];
        int[] array = new int[n+1];
        dp[0][0] = 0;
        // 가치: array[i]*i
        for (int i = 1; i <= n; i++) {
            array[i] = Integer.parseInt(br.readLine());
        }
		// 초기 테이블 세팅
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + (long) array[i] * i;
            dp[0][i] = dp[0][i-1] + (long)array[n-i+1]*i;
        }
		// 로직
        long ans = 0;
        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <=n; j++) {
                if(i+j>n) continue;
                dp[i][j] =Math.max(dp[i][j],dp[i-1][j]+ (long) (i + j) *array[i]);
                dp[i][j] =Math.max(dp[i][j],dp[i][j-1]+(long) (i+j)*array[n-j+1]);

                if(i+j==n)
                    ans = Math.max(ans,dp[i][j]);
            }
        }
        // 출력
        if(n==1)
            System.out.println(array[n]);
        else System.out.println(ans);
    }
}

 

 

 

 

 

'PS > Gold' 카테고리의 다른 글

[Gold V] 토마토 - 7576 (Java)  (0) 2023.08.02
[Gold III] Happy Cow - 13002 (Java)  (0) 2023.07.29
[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29
[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27
[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25

+ Recent posts