Study/Baekjoon

Baekjoon11053: 가장 긴 증가하는 부분 수열, 3가지 풀이

devyoseph 2021. 10. 26. 06:02

가장 긴 증가하는 부분 수열

시간 제한메모리 제한제출정답맞은 사람정답 비율

1 초 256 MB 84740 32987 21636 37.047%

문제

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다.

 

풀이1

재귀함수를 사용해서 풀이할 것인데, [ 작은 숫자 → 큰 숫자 ] 의 개념을 [ 큰 숫자 → 작은 숫자 ] 탐색으로 생각해본다.

 

[ 10, 20, 10, 30 ]으로 먼저 축소해서 생각해본다

30은 왼쪽에 10과 20을 선택할 수 있다

첫번째 경우: 10보다 작은 수가 없기에 2가 된다

두번째 경우: 20을 선택하면 더 작은 수 10이 존재하기에 길이가 3이 될 수 있다.

 

[ 10, 20, 10, 30, 20, 50]

50인 위치에 있을 때, 자기보다 왼쪽에 있고 작은 수 중에 최대값 하나를 선택하고 1을 더해 가져온다.

최대값을 가지는 항은 → 4항(30)=3이다

30은 자기보다 왼쪽에 있고 작은 수 중 최대값 하나를 선택하고 1을 더해 가져온다.

최대값을 가지는 항은 → 2항(30)=2이다

20은 자기보다 왼쪽에 있고 작은 수 중 최대값 하나를 선택하고 1을 더해 가져온다.

최대값을 가지는 항은 → 1항(10)=1이다

10은 자기보다 왼쪽에 작은 수가 없으므로 1을 반환한다

 

자신보다 작은 수가 있고 그 중에서 최대값을 더하는 작업을 했다

그 중에서 최대값만을 출력해야하는데 현존 최대값을 기록하기 위해 배열의 공간을 하나 더 사용한다

반복문 안에서 지금까지의 값 중 최대값만을 가져와 배열에 넣는다

 

배열의 크기는 [N][3]이 된다. 입력받는 N값의 크기와 [해당 숫자][자기보다 작은 수 중 최대값+1][현존 최대값]

각각 dp[N][0], dp[N][1], dp[N][2]에 들어간다. 반복문을 이용해 각각의 값을 구할 수 있다.

dp[N][0]: 값을 비교할 숫자들

dp[N][1]: ( 지금 항보다 작은 항 ) + [해당 숫자]가 더 작은 값 중 최대값을 구하고 1을 더해 집어넣은 값

dp[N][2]: 현재까지 구한 dp[N][1] 중 최대값을 저장

 

재귀

import java.util.*;
public class Main {
	static Integer[][] dp;
	static Integer[] increase(int N) {
		
		if(dp[N][1]==null || dp[N][2]==null ) {
			int max = 0;
			int out =0;
			boolean min = true;
			for(int i=0; i<N ; i++) {
				if(dp[i][0]<dp[N][0]) {
					min = false; //한번이라도 작은값이 있으면 최소값이 아니다
					max = Math.max(increase(i)[1], max);
		//자신보다 작고 왼쪽에 있는 값들에 대해서 최대값을 가져옴
				}
				out=Math.max(increase(i)[2], out);
			}
			if(min) { 
				dp[N][1]=1; //최소값인 경우 본인은 1을 가짐
			} else {
				dp[N][1]=max+1;
        //최소값이 아닌 경우 왼쪽 값의 최대값을 더함
			}
			if(out<=dp[N][1]) {
				dp[N][2]=dp[N][1];
			} else dp[N][2]=out;
	   }
		
		return dp[N];
	}
public static void main(String[] args){
	 Scanner sc = new Scanner(System.in);
	 int N = sc.nextInt();
	 dp = new Integer[N][3];
     //0은 숫자, 1은 그 항이 가지는 값, 2는 그 때까지의 최대값
	 dp[0][1]=1;
	 dp[0][2]=1;
	 for(int i=0; i<N;i++) {
		 dp[i][0]=sc.nextInt();
	 }
	 System.out.print(increase(N-1)[2]);
}}

 

풀이2

반복문을 이용하여 풀이한다. 증가하는 순열이기에 같이 증가하는 반복문을 사용하면 더 효율적이다

모든 값을 배열에 넣었다고 가정한다. dp[N]값에는 기본값으로 1이 주어진다.

1) 1번항보다 오른쪽에 있는 수 중 1번항보다 큰 모든 수에 1을 더한다.

2) 2번항의 경우

        i) 2번항보다 오른쪽에 있는 값들과 비교한다. (2번항 vs 탐색항)

       ii) 2번항보다 탐색항이 큰 숫자인 경우 2번항 + 1 을 한 값과 현재 탐색항이 가지고 있는 값과 비교해 큰 값을 넣는다

3) n항의 경우

        i) n번항보다 오른쪽에 있는 값들과 비교한다

       ii) 탐색항이 n번항보다 큰 숫자인 경우 n번항+1을 한 값과 탐색항의 값을 비교해 더 큰 값을 넣는다

 

이중배열 + 다음 항

import java.util.*;
public class Main {
public static void main(String[] args){
	 Scanner sc = new Scanner(System.in);
	 int N = sc.nextInt();
	 int[][]dp=new int[N][2]; //효율을 위한 이중배열 사용
	 int max=0;
	 for(int i=0; i<N;i++)dp[i][0]=sc.nextInt(); //값을 집어넣기
	 for(int i=0; i<N;i++){
		 for(int j=i; j<N; j++) {
			 if(dp[j][0]>dp[i][0])dp[j][1]=Math.max(dp[i][1]+1, dp[j][1]);
             //i보다 j항이 큰 수를 가질 경우: i+1과 현재 j가 가지고 있는 값 중 큰 값을 넣어줌
			 max=Math.max(dp[j][1],max);}}
	 System.out.print(max+1);
}}

풀이3

코드를 더 짧게 만들기 위해 값을 넣어주며 탐색한다.

새로 값을 넣어주고 현재까지의 항들과 비교해 새로운 값이 더 큰 값이면 지금 가지고 있는 값과 이전항 값의+1을 비교해 더 큰 수를 넣는다.

 

이중배열 + 이전 항

import java.util.*;
public class Main {
public static void main(String[] args){
	 Scanner sc = new Scanner(System.in);
	 int N = sc.nextInt();
	 int[][]dp=new int[N][2];
	 int max=0;
	 for(int i=0; i<N;i++) {
		 dp[i][0]=sc.nextInt();
		 for(int j=0; j<=i; j++) {
			 if(dp[j][0]<dp[i][0])dp[i][1]=Math.max(dp[j][1]+1, dp[i][1]);
			 max=Math.max(dp[i][1],max);}}
	 System.out.print(max+1);
}}

'Study > Baekjoon' 카테고리의 다른 글

Baekjoon9251: LCS  (0) 2021.10.26
Baekjoon11054: 가장 긴 바이토닉 부분 수열  (0) 2021.10.26
Baekjoon9461: 파도반 수열  (0) 2021.10.26
Baekjoon1904: 01타일  (0) 2021.10.26
Baekjoon9184: 신나는 함수 실행  (0) 2021.10.25