Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

15-jung0115 #169

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open

15-jung0115 #169

wants to merge 15 commits into from

Conversation

jung0115
Copy link
Member

@jung0115 jung0115 commented Sep 27, 2024

➡️ 문제 풀이 코드

🔗 문제 링크

백준 | 이분 탐색 - 가장 긴 증가하는 부분 수열 2(12015)

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

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

[ 입력 ]
첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다.
둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)

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

✔️ 소요된 시간

1시간

✨ 수도 코드

수열을 첫번째 인덱스부터 탐색하면서 앞수보다 크면 배열에 집어넣어서 제일 긴 수열을 찾을 수 있는데, 작을 때 처리하는 부분이 까다로웠습니다

10 30 60 20 30 50

위 수열로 예를 들어 설명하면,

10 30 60

10, 30, 60까지는 증가하기 때문에 배열에 집어넣어줍니다.

그리고 다음 20은 60보다 작기 때문에 주의해야 합니다!
현재 배열에서 20보다 작은 수 중 가장 뒤쪽에 있는 수는 10입니다.

10 20 60

그러므로 10 뒤의 수를 20으로 대치해줍니다.

그 이유와 방법이 중요한데,
우선 이 문제는 증가하는 수열을 구하는 게 아니라 수열의 길이를 구하는 문제이기 때문에 최대로 가능한 길이만 알면 됩니다.

길이가 최대가 되려면 각 숫자는 최대한 뒤쪽에 위치해야 합니다.
예를 들어, 60은 두번째에 있어도 증가하는 수열이 만족하지만 세번째에 있을 때 가장 길어지고, 그것이 곧 최대한 뒤쪽에 위치하는 것입니다.

그러니 각 숫자마다 최대한 뒤에 있을 수 있는 위치를 찾아주는 것인데,
그 과정에서 30을 20으로 대치한 이유를 설명하자면
만약 20과 30 사이의 숫자라면 20의 뒤에 올 수 있고, 30의 이상이라면 20과 30 모두의 뒤에 올 수 있습니다.
20으로 대치하지 않고 30을 그대로 둔다면 20과 30 사이의 숫자는 20 뒤에 오지 못하고 20의 자리에 갈 것입니다. 그러면 최대한 뒤에 배치할 수 없게 되는 거죠! 때문에 더 작은 수인 20으로 대치해주는 것입니다
(이 부분 설명이 모호한 것 같아서 코멘트로 추가 질문 주시면 답변 드리겠습니다!!)

그렇다면 그 위치는 어떻게 찾을까요?
바로 이분탐색을 사용하게 됩니다!

left를 0, right를 현재 배열 길이로 하고,
mid가 현재 탐색값보다 작다면 mid의 오른쪽에 그 위치가 있다는 뜻이고
크거나 같다면 왼쪽 또는 mid 자리에 있다는 뜻이 됩니다.

때문에 이분탐색을 이용해서 현재 탐색 값보다 작은 숫자 중 가장 뒤에 위치한 수를 찾고, 그 뒤에 현재 탐색 값을 대치해주면 됩니다.

해당 과정을 반복하면 마지막에 배열의 길이가 가장 긴 증가하는 부분 수열의 길이가 됩니다

✅ 최종 코드

public class Baekjoon_12015 {
  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    int N = Integer.parseInt(br.readLine());
    StringTokenizer st = new StringTokenizer(br.readLine());

    int[] A = new int[N];
    for(int i = 0; i < N; i++) {
      A[i] = Integer.parseInt(st.nextToken());
    }

    int[] longIncrease = new int[N];
    int lastIndex = 0;
    longIncrease[lastIndex] = A[0];

    for(int i = 1; i < N; i++) {
      int num = A[i];

      // 이전값보다 큼 -> 증가
      if(longIncrease[lastIndex] < num) {
        longIncrease[++lastIndex] = num;
      }
      // 이전값보다 작음
      else {
        int left = 0;
        int right = lastIndex + 1;

        while (left < right) {
          int mid = (left + right) / 2;

          if(longIncrease[mid] < num) {
            left = mid + 1;
          }
          else {
            right = mid;
          }
        }

        // num보다 작은 값 중 제일 뒤에 있는 숫자의 뒤
        longIncrease[left] = num;
      }
    }

    System.out.print(lastIndex + 1);
  }
}

📚 새롭게 알게된 내용

이분 탐색을 이렇게도 사용할 수 있다니... 😳 알고리즘 개념 학습에서 멈추지 않고 응용 학습을 열심히 해야겠습니다...!

Copy link
Member

@janghw0126 janghw0126 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 문제를 이중 for문을 사용해서 푸니까 시간초과가 나더라고요. 그래서 저도 이분탐색을 생각하였습니다!

dp방식과 비슷하게 앞에서부터 순회하면서 새로 정의한 dp 리스트의 마지막 값과 arr을 비교하여 arr가 더 크다면 dp에 추가하고 작거나 같다면 이분 탐색을 이용하여 인덱스 값을 구해주었습니다.

def binary_search(val):
    left, right = 0, len(lis) - 1
    while left <= right:
        mid = (left + right) // 2
        if lis[mid] < val:
            left = mid + 1
        else:
            right = mid - 1
    return left

n = int(input())
sequence = list(map(int, input().split()))
lis = [sequence[0]]

for i in range(1, n):
    if lis[-1] < sequence[i]:
        lis.append(sequence[i])
    else:
        idx = binary_search(sequence[i])
        lis[idx] = sequence[i]

print(len(lis))

시간초과 때문에 이분탐색을 구현해내기까지 시간이 많이 걸렸네요😂
다른 분들의 풀이를 보니까 이 문제에서 사용되는 알고리즘이 LIS 알고리즘이라고 합니다. 참고하시면 좋을 듯 하네요!

@jung0115
Copy link
Member Author

jung0115 commented Oct 8, 2024

시간초과 때문에 이분탐색을 구현해내기까지 시간이 많이 걸렸네요😂 다른 분들의 풀이를 보니까 이 문제에서 사용되는 알고리즘이 LIS 알고리즘이라고 합니다. 참고하시면 좋을 듯 하네요!

@janghw0126 단순히 이분탐색이라고만 생각했는데, LIS 알고리즘을 쓴 거였군요! 혜원 님 덕분에 개념적으로도 이해하고 넘어가게 된 것 같습니다 ☺️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants