코딩테스트/백준

[백준 - 15961] 회전 초밥 .java

머밍 2025. 6. 9. 16:41

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

 

  • 같은 종류 초밥 여러개 가능
  • 서로 다른 종류의 k개 접시를 연속해서 먹을 수 있음
  • 종류가 c인 초밥을 한번 먹을 수 있음
  • 이때 현재 연속된 k개의 접시들 중 c종류 접시가 없으면 종류가 하나 추가됨
  • => 먹을 수 있는 초밥의 서로 다른 종류 개수의 최댓값을 구하기
  • 원형으로 접시에 접근해야함

 

풀이

고정 길이인 k만큼 윈도우를 설정

=> 초기 윈도우 설정하고 하나씩 이동하며 하나 추가, 하나 삭제

  • dishes 배열: 입력받은 순서대로 접시 종류를 저장
  • dishCount 배열: 초밥 종류는  2 ≤ d ≤ 3,000 -> 인덱스를 접시 종류로,  값을 현재 윈도우에 속한 초밥 종류의 빈도로 함

=> dishCount의 인덱스가 dishes배열의 값이 되어야함

 

초기 윈도우 설정

후위 연산자를 사용하여 하나 증가하기 전의 현재 접시 빈도가 0이면 없는 종류기 때문에 하나 추가

 

 

추가

if(dishCount[dishes[(i+k-1)%n]]++ == 0) kind++;

i-k+1번째를 추가하면서 원형으로 접근하기 위해 %n

추가하려는 인덱스가 현재 윈도우에 없는 종류라면 하나 추가 -> 후위 연산자 이용

 

 

정답 - 최댓값 갱신

answer = Math.max(answer,kind + (dishCount[c]==0? 1:0));

현재 윈도우가 가지는 종류에 쿠폰에 적힌 접시가 없으면 종류에 하나 추가

만약 있으면(빈도가 0이 아니면) 추가 불가

 

삭제

if(--dishCount[dishes[i]] ==0) kind--;

전위 연산자를 사용하여 빈도를 하나 줄였을 때의 값이 0이라면 

현재 윈도우에 더이상 존재하지 않는 종류기 때문에 종류를 하나 감소

 



import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int d = sc.nextInt();
        int k = sc.nextInt();
        int c = sc.nextInt();

        int[] dishes = new int[n];
        for(int i = 0;i < n;i++) {
            dishes[i] = sc.nextInt();
        }

        int[] dishCount = new int[d+1];
        int kind =0;

        for(int i = 0; i<k-1;i++){
            //하나 증가하기 전이 0이면 없던 종류니까 추가
            if(dishCount[dishes[i]]++ == 0){
                kind++;
            }
        }

        int answer = 0;

        for(int i =0; i<n;i++){
            if(dishCount[dishes[(i+k-1)%n]]++ == 0) kind++;

            answer = Math.max(answer,kind + (dishCount[c]==0? 1:0) );

            if(--dishCount[dishes[i]] ==0) kind--;
        }

        System.out.println(answer);

    }
}

 

 

 

길이가 고정된 윈도우(크기 K)

를 한 칸씩 밀면서 탐색할 때

 

  • 초기 윈도우 세팅

윈도우에 들어갈 첫 K 개 요소(혹은 필요한 경우 K–1개)를 미리 처리해 두고,그 상태를 기준으로 시작

 

  • 매 스텝마다 한 요소 추가 + 한 요소 제거

윈도우의 오른쪽 새로 들어올 요소 하나를 추가(add)

윈도우의 왼쪽 빠져나갈 요소 하나를 제거(remove)

(고정된 크기 K를 유지하면서) 이때마다 필요한만큼 O(1)로 갱신

 

=> 윈도우를 매번 처음부터 다시 계산하는 대신 전체를 O(N) 시간에 한 번만 쭉 이어지면 됨