https://www.acmicpc.net/problem/13398
13398번: 연속합 2
첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.
www.acmicpc.net
코드설명
DP 문제입니다.
이 문제에서 어려웠던점은 숫자 하나를 제거할 수 있다는 것입니다.
dp[N+1][2] 를 선언하여 [][0] : 아직 한번도 수를 제거하지않은경우, [][1] : 수를 한번 제거한경우 로 나누어서 해결합니다.
핵심로직입니다.,
dp[1][0] = arr[1]; dp[1][1] = arr[1]; answer = arr[1]; for(int i=2;i<=N;i++) { dp[i][0] = Math.max(arr[i], dp[i-1][0] + arr[i]); //특정수를 제거하지 않을 경우 dp[i][1] = Math.max(dp[i-1][0], dp[i-1][1] + arr[i]); // i번쨰 수를 제거하므로 dp[i-1][0]만 더해줍니다. } 혹은 dp[1][0] = arr[1]; dp[1][1] = arr[1]; answer = arr[1]; for(int i=2;i<=N;i++) { dp[i][0] = Math.max(arr[i], dp[i-1][0] + arr[i]); //특정수를 제거하지 않을 경우 dp[i][1] = Math.max(dp[i-2][0] + arr[i], dp[i-1][1] + arr[i]); //dp[i-2][0] + arr[i] 로 바꿔서 처리 }
가장 주의깊게 봐야할것은 dp[i][1] 입니다. dp[i-1][0] 에서 arr[i]를 더하는 것을 제거한다는점을 생각합니다.
가장 의문이 생기는점은 초기화값으로 dp[1][1] = arr[1] 로 왜 설정하는지 의문이 생깁니다.
왜냐하면, 첫번쨰 dp값에서 특정수로 첫번쨰 값을 제거한 경우가 존재할텐데 dp[1][1] 에서 첫번쨰 수를 제거했다면 0 이어야하는것 아닌가라고 생각이 들기 떄문입니다.
아직 정확한 이유를 알지 못하겠으나, 추측한바는 이렇게 초기화 함으로써 이후에 연산에서 원활히 사용할 수 있습니다.
이때 만약 dp[i][1] = 0 으로 설정할경우에는 음수인 경우를 고려하지 못하므로 dp[1][1] = arr[1] 로 설정해야만 합니다.
또한 이러한 조건을 통하여
3 -3 -2 -1 값 : -1 모두 음수일경우를 고려 https://www.acmicpc.net/board/view/87254
문제에서 유의해야할점은, 음수값이 나올 수 있기에
answer값이 = 0이 아닌 answer = arr[1]로 초기화하여 진행합니다.
위에처럼 dp[][] 배열을 나눠서 하지않고,
앞에서부터 DP1 값을 구하고 뒤에서부터 DP2 값을 구한뒤,
DP1[i-1] + DP2[i+1] 을 구함으로써 DP[i]를 제거한 값을 구할 수 있습니다.
코드
정답코드2
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; public class Main { public static int N; public static int[][] dp; public static int[] arr; public static int answer; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); dp = new int[N+1][2]; //[][0] : 아직 한번도 수를 제거하지않은경우, [][1] : 수를 한번 제거한경우 arr = new int[N+1]; st = new StringTokenizer(br.readLine()); for(int i=1;i<=N;i++) { arr[i] = Integer.parseInt(st.nextToken()); } dp[1][0] = arr[1]; dp[1][1] = arr[1]; answer = arr[1]; for(int i=2;i<=N;i++) { dp[i][0] = Math.max(arr[i], dp[i-1][0] + arr[i]); //특정수를 제거하지 않을 경우 dp[i][1] = Math.max(dp[i-2][0] + arr[i], dp[i-1][1] + arr[i]); // i번쨰 수를 제거하므로 dp[i-1][0]만 더해줍니다. } for(int i=1;i<=N;i++) { answer = Math.max(answer, Math.max(dp[i][0], dp[i][1])); } System.out.println(answer); } }
정답코드 1
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; public class Main { public static int N; public static int[][] dp; public static int[] arr; public static int answer; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); dp = new int[N+1][2]; //[][0] : 아직 한번도 수를 제거하지않은경우, [][1] : 수를 한번 제거한경우 arr = new int[N+1]; st = new StringTokenizer(br.readLine()); for(int i=1;i<=N;i++) { arr[i] = Integer.parseInt(st.nextToken()); } dp[1][0] = arr[1]; dp[1][1] = arr[1]; answer = arr[1]; for(int i=2;i<=N;i++) { dp[i][0] = Math.max(arr[i], dp[i-1][0] + arr[i]); //특정수를 제거하지 않을 경우 dp[i][1] = Math.max(dp[i-1][0], dp[i-1][1] + arr[i]); // i번쨰 수를 제거하므로 dp[i-1][0]만 더해줍니다. } for(int i=1;i<=N;i++) { // System.out.println(dp[i]); answer = Math.max(answer, Math.max(dp[i][0], dp[i][1])); } System.out.println(answer); } }
아래의 코드는 잘못되었다.
이유는 수열에서 수를 하나 제거할 수 있다. (제거하지 않아도 된다) 는 조건을 올바르게 구현하지 않았습니다.
아래와 같이 구현할경우 한번 이상 삭제할 가능성이 있기 떄문이다.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; public class Main { public static int N; public static int[] dp; public static int[] arr; public static int answer; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); dp = new int[N+1]; arr = new int[N+1]; st = new StringTokenizer(br.readLine()); for(int i=1;i<=N;i++) { arr[i] = Integer.parseInt(st.nextToken()); } dp[1] = arr[1]; for(int i=2;i<=N;i++) { dp[i] = Math.max(arr[i], dp[i-1] + arr[i]); dp[i] = Math.max(dp[i], dp[i-2] + arr[i]); // System.out.println(dp[i]); } dp[1] = arr[1]; for(int i=1;i<=N;i++) { System.out.println(dp[i]); } } }
기본 연속합 코드 (숫자 하나를 제거하는것을 고려하지 않은 코드)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.math.BigInteger; import java.util.*; public class Main { public static int T, N, M; public static int answer = 0; public static int[] arr; public static int[] dp; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); arr = new int[N]; dp = new int[N]; st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { arr[i] = Integer.parseInt(st.nextToken()); } dp[0] = arr[0]; answer = dp[0]; for(int i=1;i<N;i++) { dp[i] = Math.max(dp[i-1] + arr[i], arr[i]); answer = Math.max(answer, dp[i]); } System.out.println(answer); } }
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 2210 숫자판 점프 - DFS(깊이우선탐색) + BFS(너비우선탐색) + 완전탐색(BruteForce) JAVA (0) | 2023.09.17 |
---|---|
[백준] 16922 로마 숫자 만들기- 브루트포스 JAVA (0) | 2023.09.16 |
[백준] 11722 가장 긴 감소하는 부분 수열 - 동적계획법(Dynamic Programming, DP) + LDS(Longest Decreasing Subsequence) JAVA (0) | 2023.09.15 |
[백준] 11057 오르막 수 - DP JAVA (0) | 2023.09.15 |
[백준] 1309 동물원 - 동적계획법(Dynamic Programming, DP) JAVA (0) | 2023.09.14 |