3 분 소요



https://www.acmicpc.net/problem/30804



📌 문제

문제 유형

  • 구현
  • 브루투포스
  • 투 포인터


문제 설명

N개의 과일이 순서대로 꽂힌 막대에서 앞과 뒤에서 몇 개를 제거해,
과일 종류가 최대 2종류인 연속 부분을 만들려고 한다.

과일의 개수가 가장 많은 경우의 수를 구하시오.


입력

첫 줄에 과일의 개수

둘째 줄에 탕후루에 꽂힌 과일을 의미하는 n개의 정수

5
5 1 1 2 1

출력

문제의 방법대로 만들 수 있는 과일을 두 종류 이하로 사용한 탕후루 중에서, 과일의 개수가 가장 많은 탕후루의 과일 개수를 첫째 줄에 출력하세요.

4


⚠️ 문제에서는 “앞과 뒤에서 과일을 몇 개 빼서” 과일 종류가 최대 2개인 구간을 만들라고 했지만,

  • 나는 “슬라이딩 윈도우(투 포인터)”를 이용해 전체 배열에서 과일 종류가 2개 이하인 가장 긴 연속 구간을 찾는 방식으로 해결했다.
  • 따라서 슬라이딩 윈도우 방식을 사용하면 문제처럼 앞·뒤에서 자르지 않고 해결이 가능하다.



🔍 문제 풀이

과일 종류가 2개 이하인 가장 긴 연속 부분 수열을 구하면 된다.!

  1. Map을 사용해 과일의 종류와 등장 횟수 저장
  2. 만약 key(과일 종류의 개수)가 3이 되면 -> 구간 축소 시작
    • 현재 구간의 맨 앞 과일의 개수를 1 줄이기
    • 과일 개수가 0이 되면, 해당 과일은 현재 구간에 더 이상 존재하지 않으므로 -> fruitKind 맵에서 완전히 제거 (과일 종류 감소)
    • 그 후, left++를 통해 윈도우의 시작 위치를 한 칸 오른쪽으로 이동
  3. 현재 구간 길이(right - left + 1)와 최대값 비교 후 갱신


예제1: fruits = [1, 1, 1, 2, 3]

  • 초기 상태: fruitKind = {1=3, 2=1, 3=1} → 종류 3개 → while 진입
  • while 루프 흐름
    • left = 0, fruits[left] = 1
      • => count 3 → 2
      • => left++ 구간: [1, 1, 2, 3]

    • left = 1, fruits[left] = 1
      • => count 2 → 1
      • => left++ → 구간: [1, 2, 3]

    • left = 2, fruits[left] = 1
      • => count 1 → 0 → map에서 제거,
      • => left++ → 구간: [2, 3]


⚠️ left++는 반드시 count 감소 이후에 실행되어야 함 -> 그렇지 않으면 구간 맨 앞의 과일을 카운트에서 누락하게 됨


예제 2: fruits = [5, 1, 1, 2, 1, 5]



📌 놓친 점

과일 개수 감소는 곧바로 종류 감소가 아님

처음에는 과일 종류가 3개가 되었을 때, 한 번만 과일을 제거하면 바로 종류가 줄어든다고 생각했다.

하지만 count 값이 1 이상인 상태에서 1만 줄이면 여전히 구간에 존재하기 때문에 kind는 그대로 유지된다.


처음엔 left++만으로 종류가 줄어들 거라 생각했지만, value가 0이 되어야 Map에서 remove가 일어나며 종류 수 감소가 일어난다.

  • fruitKind.put(fruits[left], fruitKind.get(fruits[left]) - 1);
    • 맨 앞 과일의 개수(value) 를 줄이는 작업이다.
    • e.g., {5=2}{5=1}


  • left++;
    • 구간의 시작 위치를 오른쪽으로 한 칸 이동하는 것
    • e.g., 5 1 1 2 11 1 2 1 (5가 제거됨)


과일 종류가 2개를 초과했을 때, 이 둘은 같이 실행되어 윈도우를 축소한다.

이 두개가 헷갈려 이해하는데 많은 시간이 소요됐다.


left++는 무조건 반복

과일 개수를 줄이더라도 종류가 여전히 3개이면 left++는 계속 수행되어야 한다.

결국 특정 과일의 개수가 0이 되어야 종류가 감소하고 while을 빠져나올 수 있다.



💻 전체 코드

import java.io.*;
import java.util.*;

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

        int n = Integer.parseInt(br.readLine());

        int[] fruits = new int[n]; // 과일 종류 저장

        // 과일 입력
        StringTokenizer st = new StringTokenizer(br.readLine());
        for(int i=0; i<n; i++){
            fruits[i] = Integer.parseInt(st.nextToken());
        }

        int left = 0;
        int maxLan = 0;


        // key: 과일 종류, value: 해당 과일의 빈도 수
        // e.g., 입력이 5 1 1 2 1일 때 → fruitKind = {5:1, 1:3, 2:1}
        HashMap<Integer, Integer> fruitKind = new HashMap<>(); // 과일 종류별 개수 저장

        for(int right = 0; right<n; right++){
            // 현재 과일 추가 (없으면 0으로 시작해서 +1 해주기)
            fruitKind.put(fruits[right], fruitKind.getOrDefault(fruits[right], 0) + 1);

            // 과일 종류가 2 초과할경우 -> 왼쪽부터 줄이기
            while(fruitKind.size() > 2){

                // 현재 구간의 맨 왼쪽 과일 개수 1 감소
                fruitKind.put(fruits[left], fruitKind.get(fruits[left]) - 1); // 예: {5:1} → {5:0}

                // 개수가 0이 되면 map에서 제거 -> 종류수 감소
                if(fruitKind.get(fruits[left]) == 0) fruitKind.remove(fruits[left]); // 예: {5:0} → 제거 → 남은 구간: 1 1 2 1

                // 왼쪽 포인터 오른쪽으로 이동 -> 구간축소
                left++;
            }

            // 현재 구간의 최대 길이 갱신
            maxLan = Math.max(maxLan, right - left + 1);
        }
        System.out.println(maxLan);

    }
}


카테고리:

업데이트:

댓글남기기