[백준] 15565번: 귀여운 라이언 [C/C++]

2023. 10. 3. 00:23·개발/백준 문제풀이

# 문제

15565번: 귀여운 라이언
https://www.acmicpc.net/problem/15565

15565번: 귀여운 라이언

꿀귀 라이언 인형과, 마찬가지로 꿀귀인 어피치 인형이 N개 일렬로 놓여 있다. 라이언 인형은 1, 어피치 인형은 2로 표현하자. 라이언 인형이 K개 이상 있는 가장 작은 연속된 인형들의 집합의

www.acmicpc.net

 


# 접근

라이언 인형( = 1)이 k개 이상 있는 가장 작은 연속된 인형들의 집합을 찾아야 한다. 집합의 크기를 명시해 주지 않기 때문에, 이중 for문을 이용한 브루트 포스 식의 순회가 불가능하다(사실 가능하다 할지라도, N의 크기가 106이기 때문에 시간복잡도에서 걸릴 것이다). 따라서 새로운 접근 방식을 생각해야 한다.

 

이럴 때 필요한 것이 바로 투 포인터 알고리즘이다. 투 포인터란 주어진 수열에서 특정한 성질을 가지는 부분 수열을 찾을 때 활용되는 방식으로, 각각 부분 수열의 처음과 끝을 나타내고 수열의 인덱스를 가리키는 변수 L과 R을 만들고 그 L과 R을 주어진 조건에 따라 움직여 가며 적절한 부분 수열을 찾아내는 알고리즘이다. L을 앞으로 한 칸 움직였다면 그만큼 합에서 빼고(시간복잡도: O(1)), R을 앞으로 한 칸 움직였다면 그만큼 합에 더하면(시간복잡도: O(1)) 된다. 이렇게 시간복잡도를 획기적으로 줄일 수 있는 기법이다.
(※ 시간복잡도 개념을 모른다면 그냥 코드가 얼마나 효율적으로, 빠르게 실행되는지를 나타내는 단위라고 생각하자.)
 
이 문제에선
1. 라이언 인형의 개수가 k보다 작다면 R 포인터를 한 칸 앞으로 당긴다.
2. 라이언 인형의 개수가 k보다 같다면(이 연산 과정을 따른다면 라이언 인형의 개수가 k보다 커질 수 없다.) 현재 부분 수열의 길이를 지금까지 기록된 최소 부분 수열의 길이와 비교한다. 그 다음 L 포인터를 한 칸 앞으로 당긴다.
3. 1과 2를 반복한다.
 
이 로직을 떠올리고 투 포인터 개념을 이용해 이를 구현하는 것이 핵심이다.


# 풀이

#include <stdio.h>
int main() {
  int arr[1000005];
  int n,k,length;
  int a=0;
  int b=0; //a = L포인터, b = R 포인터
  int min=2100000000;
  scanf("%d %d", &n, &k);
  for (int i=1;i<n+1;i++) {
    scanf("%d", &arr[i]);
  }
  
  arr[0]=0; //입력은 arr[1] 부터 받고, arr[0]은 0으로 남겨 계산이 좀 더 용이하도록 했다.

  
  int sum=0;
  while(1) {
    if(b==n+1) { //포인터 R이 끝에 도달한다면 반복 끝내기
      break;
    }
    if (sum == k) { //라이언 인형의 개수가 k인 경우
      length=b-a;
      min=(length<min)?length:min;
      a++; //나는 라이언 인형의 개수를 L 포인터가 가리키는 인덱스의 다음 인덱스 부터 세는 방식으로 구현했다.
      sum=(arr[a]==1)?sum-1:sum;
    }
    else { //라이언 인형의 개수가 k보다 작은 경우
      b++;
      sum=(arr[b]==1)?sum+1:sum;
    }
    
  }
  min=(min==2100000000)?-1:min;
  printf("%d", min);
}

나는 문제의 의도대로 충실하게 풀었으나, 라이언( = 1)인지 어피치( = 2)인지 구분하고 sum에서 더하고 빼는 것이 은근 까다롭고 실수가 많이 일어날 만한 부분이다. 이를 보완하기 위해 입력을 받을 때부터 1이라면 1로 입력받고, 2라면 0으로 입력받아 그냥 합 자체를 세는 것도 괜찮은 방식이라고 생각한다.


# 맞는데 왜 틀림?

이 문제에서 맞는데 왜 틀렸는지 의문이 들 경우, 다음의 사항을 확인해보자.
 
- 반복문을 실행하기 전, min을 설정하지 않았거나, 충분히 크게 설정하지 않음.
- 반복문을 끝내는 과정에서 실수가 있음.
- 포인터를 움직이기 전에 sum에 +1, -1을 했거나, 반대의 경우.


# 정리

★★★★☆
실버 난이도에서 어려운 축에 속하지만 투 포인터를 이해하고 기초 문제를 조금만 풀어본다면, 아이디어 자체는 쉽게 떠올릴 수 있는 기초 응용 문제.
 
하지만 사소한 부분에서 실수할 여지가 많기도 한듯.

'개발 > 백준 문제풀이' 카테고리의 다른 글

[백준] 30052번: 거리 두기 게임 [C/C++]  (2) 2023.10.05
'개발/백준 문제풀이' 카테고리의 다른 글
  • [백준] 30052번: 거리 두기 게임 [C/C++]
시계태엽
시계태엽
지극히 개인적인 취향의 설파, 근데 프로그래밍을 곁들인.
  • 시계태엽
    시계태엽의 블로그
    시계태엽
  • 전체
    오늘
    어제
    • 분류 전체보기 (14)
      • 블로그 (1)
      • 일기 (10)
        • 후기 (2)
        • 사색 (8)
      • 개발 (3)
        • 백준 문제풀이 (2)
        • 개인 개발 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    생일
    의미심장
    C++
    간석기
    캡슐토이
    생일선물추천
    PS
    트럼프양말
    나이테
    인스타그램
    귀여운라이언
    문제풀이
    음악추천
    홍대병
    단보
    투포인터
    장패드
    황새오래걷기
    백준귀여운라이언
    백준
    백준30052
    볼링화
    거리두기게임
    역지사지
    백준거리두기게임
    너였다면
    원자력병원
    백준15565
    C
    3모
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
시계태엽
[백준] 15565번: 귀여운 라이언 [C/C++]
상단으로

티스토리툴바