* 핵심
2가지의 경우의 수를 생각해서 Top-Down 방식 사용
1. 왼쪽이 오른쪽보다 높은 경우 => 오른쪽 depth+1, dp[left][right]에 오른쪽 카드 값 추가
2. 왼쪽이 오른쪽보다 낮은 경우 => 왼쪽 depth+1 과 왼쪽 depth+1, 오른쪽 depth+1 값 중 max값 비교
최종적으로 1번값과 2번값의 max값을 dp[left][right]값으로 선택
* 문제
문제
지훈이는 최근에 혼자 하는 카드게임을 즐겨하고 있다. 게임에 사용하는 각 카드에는 양의 정수 하나가 적혀있고 같은 숫자가 적힌 카드는 여러 장 있을 수 있다. 게임방법은 우선 짝수개의 카드를 무작위로 섞은 뒤 같은 개수의 두 더미로 나누어 하나는 왼쪽에 다른 하나는 오른쪽에 둔다. 그리고 빈 통을 하나 준비한다.
이제 각 더미의 제일 위에 있는 카드끼리 서로 비교하며 게임을 한다. 게임 규칙은 다음과 같다. 지금부터 왼쪽 더미의 제일 위 카드를 왼쪽 카드로, 오른쪽 더미의 제일 위 카드를 오른쪽 카드로 부르겠다.
- 언제든지 왼쪽 카드만 통에 버릴 수도 있고 왼쪽 카드와 오른쪽 카드를 둘 다 통에 버릴 수도 있다. 이때 얻는 점수는 없다.
- 오른쪽 카드에 적힌 수가 왼쪽 카드에 적힌 수보다 작은 경우에는 오른쪽 카드만 통에 버릴 수도 있다. 오른쪽 카드만 버리는 경우에는 오른쪽 카드에 적힌 수만큼 점수를 얻는다.
- (1)과 (2)의 규칙에 따라 게임을 진행하다가 어느 쪽 더미든 남은 카드가 없다면 게임이 끝나며 그때까지 얻은 점수의 합이 최종 점수가 된다.
다음 예는 세 장 씩 두 더미의 카드를 가지고 게임을 시작하는 경우이다
카드 순서 | 왼쪽 더미 | 오른쪽 더미 |
---|---|---|
1 | 3 | 2 |
2 | 2 | 4 |
3 | 5 | 1 |
이 경우, 우선 오른쪽 카드 2가 왼쪽 카드 3보다 작으므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 모두 버리거나, 규칙 (2)에 따라 오른쪽 카드만 버릴 수 있다. 만약 오른쪽 카드만 버리는 것으로 선택하면, 2만큼 점수를 얻고 오른쪽 카드 2는 버린다. 이제 오른쪽 더미의 제일 위 카드는 4이고 이는 왼쪽 카드 3보다 크므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 둘 다 버릴 수 있다. 만약 둘 다 버리는 것으로 선택하면, 이제 왼쪽 카드는 2가 되고 오른쪽 카드는 1이 된다. 이 경우 다시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 왼쪽 카드만 버리는 것으로 선택하면 이제 왼쪽 카드는 5가 되고 오른쪽 카드는 1이 된다. 이 경우에도 역시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 오른쪽 카드만 버리는 것으로 선택하면 1만큼 점수를 얻고 오른쪽 카드 1은 버린다. 이제 오른쪽 더미에는 남은 카드가 없으므로 규칙 (3)에 따라 게임이 끝나며 최종 점수는 2+1=3이 된다.
두 더미의 카드가 주어졌을 때, 게임을 통해 얻을 수 있는 최종 점수의 최댓값을 출력하는 프로그램을 작성하시오. 위 예에서 최종 점수의 최댓값은 7이다.
입력
첫 줄에는 한 더미의 카드의 개수를 나타내는 자연수 N(1 ≤ N ≤ 2,000)이 주어진다. 다음 줄에는 왼쪽 더미의 카드에 적힌 정수 A(1 ≤ A ≤ 2,000)가 카드 순서대로 N개 주어진다. 그 다음 줄에는 오른쪽 더미의 카드에 적힌 정수 B(1 ≤ B ≤ 2,000)가 카드 순서대로 N개 주어진다. 각 더미에는 같은 숫자를 가진 카드가 두 개 이상 있을 수 있다.
출력
얻을 수 있는 최종 점수의 최댓값을 출력한다.
* 소스 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import java.io.*; import java.util.*; public class Main { public static int n; public static int[][] card, dp; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); n = Integer.parseInt(br.readLine()); card = new int[n][2]; dp = new int[n][n]; // [leftDepth][rightDepth] for (int i = 0; i < n; i++) { Arrays.fill(dp[i], -1); } for (int i = 0; i < 2; i++) { StringTokenizer stk = new StringTokenizer(br.readLine()); for (int j = 0; j < n; j++) { card[j][i] = Integer.parseInt(stk.nextToken()); } } System.out.println(TopDown(0, 0)); } public static int TopDown(int leftDepth, int rightDepth) { if (leftDepth == n || rightDepth == n) return 0; //종료 조건 if (dp[leftDepth][rightDepth] != -1) return dp[leftDepth][rightDepth]; //메모이제이션 else { dp[leftDepth][rightDepth] = 0; if (card[leftDepth][0] > card[rightDepth][1]) //왼쪽이 오른쪽보다 큰 경우 dp[leftDepth][rightDepth] = TopDown(leftDepth, rightDepth + 1) + card[rightDepth][1]; dp[leftDepth][rightDepth] = Math.max(TopDown(leftDepth + 1, rightDepth + 1), Math.max(TopDown(leftDepth + 1, rightDepth), dp[leftDepth][rightDepth])); } return dp[leftDepth][rightDepth]; } } | cs |
'∙Algorithm' 카테고리의 다른 글
[Algorithm] 백준/1922 :: 네트워크 연결 (0) | 2019.01.16 |
---|---|
[Algorithm] 백준/11055 :: 가장 큰 증가 부분 수열 (0) | 2019.01.14 |
[Algorithm] 백준/2156 :: 포도주 시식 (0) | 2019.01.10 |
[Algorithm] 백준/1520 :: 내리막 길 (0) | 2019.01.10 |