ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최대 부분 증가 수열 - LIS
    Data Structures/Graph 2023. 9. 29. 17:14
    728x90

    Longest Increasing Subsequence

    나무위키

     

    최장 증가 부분 수열 - 나무위키

    어떤 임의의 수열이 주어질 때, 이 수열에서 몇 개의 수들을 제거해서 부분수열을 만들 수 있다. 이때 만들어진 부분수열 중 오름차순으로 정렬된 가장 긴 수열을 최장 증가 부분 수열이라 한다.

    namu.wiki

    최장 증가 부분 수열 문제는 동적 계획법으로 풀 수 있는 유명한 알고리즘 문제이다.

     

    어떤 임의의 수열이 주어질 때, 이 수열에서 몇 개의 수들을 제거해서 부분수열을 만들 수 있다. 이때 만들어진 부분수열 중 오름차순으로 정렬된 가장 긴 수열을 최장 증가 부분 수열이라 한다.

     

    `3 5 7 9 2 1 4 8`

    위 수열에서 몇 개의 수를 제거해 부분수열을 만들 수 있다.

     

    3 7 9 1 4 8 (5, 2 제거)
    7 9 1 8 (3, 5, 2, 4 제거)
    3 5 7 8 (9, 2, 1, 4 제거)
    1 4 8 (3, 5, 7, 9, 2 제거)

     

    이들 중 첫번째, 두번째 수열은 일부가 오름차순으로 정렬되어 있다. 반면에 세번째, 네번째 수열은 전체가 오름차순으로 정렬되어 있다.
    위와 같이 일부, 혹은 전체가 오름차순인 수열을 '증가 부분 수열’이라고 한다. 그리고 이런 증가 부분 수열 중 가장 긴 수열을
    '최장 증가 부분 수열 (LIS)'이라 한다.


    즉, 위의 수열의 집합에선 부분수열 3 5 7 8이 LIS이다. 한 수열에서 여러 개의 LIS가 나올 수도 있다.

     

    예를 들어

    수열

    5 1 6 2 7 3 8 에서

    부분수열

    1 2 3 8
    5 6 7 8 은

    모두 길이가 4인 LIS이다.


    예제

    N개의 자연수로 이루어진 수열이 주어졌을 때, 그 중에서 가장 길게 증가하는(작은 수에서 큰 수로) 원소들의 집합을 찾는 프로그램을 작성하라. 예를 들어, 원소가 2, 7, 5, 8, 6, 4, 7, 12, 3 이면 가장 길게 증가하도록 원소들을 차례대로 뽑아내면 2, 5, 6, 7, 12를 뽑아내어 길 이가 5인 최대 부분 증가수열을 만들 수 있다.

     

    ▣ 입력설명
    첫째 줄은 입력되는 데이터의 수 N(3≤N≤1,000, 자연수)를 의미하고, 둘째 줄은 N개의 입력데이터들이 주어진다.

    ▣ 출력설명


    첫 번째 줄에 부분증가수열의 최대 길이를 출력한다.

     

    ▣ 입력예제 1
    8
    53786294

     

    ▣ 출력예제 1
    4

     전체 풀이   
     import java.util.*; 
     class Main{ 	
     static int[] dy; 	
     public int solution(int[] arr){ 		
     	int answer=0; 		
     	dy=new int[arr.length]; 		
     	dy[0]=1; 		
     	for(int i=1; i<arr.length; i++){ 			
     		int max=0; 			
     		for(int j=i-1; j>=0; j--){ 				
     			if(arr[j]<arr[i] && dy[j]>max) max=dy[j]; 			
     		} 			
     		dy[i]=max+1; 			
     		answer=Math.max(answer, dy[i]); 		
     	} 		
     	return answer; 	
     }  	
     
     public static void main(String[] args){ 		
     	Main T = new Main(); 		
     	Scanner kb = new Scanner(System.in); 		
     	int n=kb.nextInt(); 		
     	int[] arr=new int[n]; 		
     	for(int i=0; i<n; i++){ 			
     		arr[i]=kb.nextInt(); 		
        } 		
     		System.out.print(T.solution(arr)); 	
        } 
     }

    전체 흐름은 위와 같다. solution 메서드에 풀이 로직이 담겨 있으니 살펴보자

    static int[] dy; 	
    public int solution(int[] arr){ 		
    	int answer=0; 		
    	dy=new int[arr.length]; 		
    	dy[0]=1; 		
    	for(int i=1; i<arr.length; i++){ 			
    		int max=0; 			
    		for(int j=i-1; j>=0; j--){ 				
    			if(arr[j]<arr[i] && dy[j]>max) max=dy[j]; 			
    		} 			
    	dy[i]=max+1; 			
    	answer=Math.max(answer, dy[i]); 		
    	} 		
    	return answer; 	
    }

     

    1. 먼저 입력으로 받은 리스트의 LIS를 구하는 것이 목표이기 때문에 dy 역시 arr와 같은 길이로 선언한다.

    1. dy[0]의 길이는 먼저 등장하는 수가 없기 때문에 확실하게 1로 크기를 잡을 수 있다.
    2. 내부 반복문은 현재 비교 대상 i와 이전에 등장한 수를 j로 하여 검증하는데 이 때 dy[j]와 max 값을 검사하는 로직을 추가하는 것으로 하나의 정의를 내릴 수 있다.
      arr[i]의 값이 이전 값들 보다 크면서 동시에 부분 수열의 크기가 가장 큰 값을 찾는다.
    3. 이렇게 가장 큰 수열의 값을 찾았다면 자기 자신까지 포함해 dy[i]=max+1의 값이 된다.
    4. 마지막으로 answer 값과 부분 증가 수열을 비교해 가장 큰 수열의 크기를 찾을 수 있다.

    예제

    import java.util.*; 
    class Brick implements Comparable<Brick>{     
    	public int s, h, w;     
    	Brick(int s, int h, int w) { 		
    		this.s = s;         
    		this.h = h;         
    		this.w = w;     
    	}     
    
    	@Override     
    	public int compareTo(Brick o){        
    	return o.s-this.s;     
    	} 
    } 
    
    class Main{ 	
    	static int[] dy; 	
    	public int solution(ArrayList<Brick> arr){ 		
    		int answer=0; 		
    		Collections.sort(arr); 		
    		dy[0]=arr.get(0).h; 		
    		answer=dy[0]; 		
    		for(int i=1; i<arr.size(); i++){ 			
    			int max_h=0; 			
    			for(int j=i-1; j>=0; j--){ 				
    				if(arr.get(j).w > arr.get(i).w && dy[j]>max_h){
    				max_h=dy[j]; 				
    				} 			
    		} 			
    		dy[i]=max_h+arr.get(i).h; 			
    		answer=Math.max(answer, dy[i]); 		
    	} 		
    		return answer; 	
    	} 
        
    	public static void main(String[] args){ 		
    		Main T = new Main(); 		
            Scanner kb = new Scanner(System.in); 		
    		int n=kb.nextInt(); 		
    		ArrayList<Brick> arr=new ArrayList<>(); 		
    		dy=new int[n]; 		
    		for(int i=0; i<n; i++){ 			
    			int a=kb.nextInt(); 			
    			int b=kb.nextInt(); 			
    			int c=kb.nextInt(); 			
    			arr.add(new Brick(a, b, c)); 		
    		} 		
    		System.out.print(T.solution(arr)); 	
    	} 
    }

     


     

     

    'Data Structures > Graph' 카테고리의 다른 글

    Tree  (0) 2023.09.29
    Graph 활용-1  (0) 2023.09.29
    Graph에 대해  (0) 2023.09.29
    Graph 검색 구현  (0) 2023.09.28
Designed by Tistory.