백준 - JAVA/동적 계획법 1
[백준] 11053번 가장 긴 증가하는 부분 수열 _ JAVA ( 주석 설명 )
wch_s
2023. 2. 24. 20:37
풀이
1) dp[i] : ary[0]~ary[i-1] 까지 탐색을 하며 ary[i]보다 작은 값의 갯수를 저장한다.
2) 식을 세워보면
for(int i=N-1;i>=0;--i){
if(ary[i]<ary[N)]{
}
}
일 때, dp[] 가 갱신된다.
3) 조건이 만족될 시
dp[N] = Math.max(dp[N], solve(i)+1);
을 통해 ary[N]보다 작은 원소의 개수를 찾는다.
4)
Top-down 방식에서는
solve(N-1)을 하면 dp[N-1]과 ary[N-1]보다 작은 값들의 dp[]가 구해지므로,
solve(0)~solve(N-1)까지 모두 실행해준다.
5)
Bottom-up 방식에서는
dp[i]<=dp[j] (i>j) 일 때의 조건도 포함시켜 => 제일 큰 값으로 유지해야 하므로
dp[0]~dp[N-1]까지 모두 구해주면 된다.
1번
→ Top-down 방식
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static int[] ary; //입력되는 수열의 값을 받는다.
static int[] dp; //i번째 값보다 작은 값들을 저장하는 배열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
int N = Integer.parseInt(br.readLine());
ary = new int[N];
dp = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;++i){
ary[i] = Integer.parseInt(st.nextToken());
}
//solve(N-1) 한 번만 하면, ary[N]을 기준으로 그보다 작은 값의 dp 만 계산될 것이다.
//따라서, 0 ~ N-1 전부 실행하면서, dp[0] ~ dp[N-1]을 구해야 한다.
for(int i=0;i<N;++i){
solve(i);
}
int dp_max = dp[0];
//dp[] 원소 중 가장 길이가 긴 부분수열의 값을 찾는다.
for(int dp_value : dp){
dp_max = Math.max(dp_max, dp_value);
}
System.out.println(dp_max);
}
//Top-down 방식
private static int solve(int N) {
if(dp[N] == 0) {
dp[N] = 1; //기본적으로 부분 수열의 길이는 1을 가지고 있다.
for (int i = N - 1; i >= 0; --i) {
//ary[N] 보다 작은 수에 대한
//1)가장 긴 부분 수열의 길이의 하나가 나온다.
//2)i-- 될수록 1)의 ary[i] 보다 큰 값이 나타나, 작은 수가 더 많아져 dp[N]이 갱신될 수 있다.
// 그러므로 1)의 값을 저장해, 다음 solve(i)+1 값과 비교한다.
if (ary[i] < ary[N]) {
//+1 을 하는 이유 : 이전 부분수열에 ary[i]보다 큰 N번째 원소가 추가되었다.
//5, 10, 20, 30, 20, 50 이라면
//N == 3 일 때
//30보다 작은 값 solve(2)을 통해 20보다 작은 값의 개수를 찾고
//10에서는 10보다 작은 값, 5에서는 5보다 작은 값
//각각 +1 을 하면서 dp[N]을 찾는다.
dp[N] = Math.max(dp[N], solve(i) + 1);
//dp[N] = solve(i) + 1; 이 안되는 이유
//N == 5 일 때
//solve(4)+1; 보다 그 다음 i인
//solve(3)+1 이 더 크다.
}
}
}
return dp[N]; //dp[N] 값이 메모제이션이 되어 있으면 바로 리턴한다.
}
}
2번
→ Bottom-up 방식
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static int[] ary; //입력되는 수열의 값을 받는다.
static int[] dp; //i번째 값보다 작은 값들을 저장하는 배열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
int N = Integer.parseInt(br.readLine());
ary = new int[N];
dp = new int[N];
st = new StringTokenizer(br.readLine());
for(int i=0;i<N;++i){
ary[i] = Integer.parseInt(st.nextToken());
}
solve2(N-1); //dp 최대 인덱스는 N-1이다.
int dp_max = dp[0];
//dp[] 원소 중 가장 길이가 긴 부분수열의 값을 찾는다.
for(int dp_value : dp){
dp_max = Math.max(dp_max, dp_value);
}
System.out.println(dp_max);
}
//Bottom-up 방식
private static int solve2(int N){
for(int i=0;i<=N;++i){
//dp[i]의 초기값은 1이다.
//기본적으로 자기 자신이 있으므로 부분 수열의 길이는 1이다.
dp[i]=1;
for(int j=0;j<i;++j){
//본인보다 작은값일 경우와
//dp값이 본인보다 클 경우 (제일 큰 값으로 유지해야 되므로)
//dp를 갱신해준다.
if(ary[i]>ary[j] && dp[i]<=dp[j])
dp[i] = dp[j]+1;
}
}
return dp[N];
}
}