문제풀이/백준

[백준] 2531번 - 회전 초밥(JAVA)

chan10 2021. 5. 11. 13:23

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

 

2531번: 회전 초밥

첫 번째 줄에는 회전 초밥 벨트에 놓인 접시의 수 N, 초밥의 가짓수 d, 연속해서 먹는 접시의 수 k, 쿠폰 번호 c가 각각 하나의 빈 칸을 사이에 두고 주어진다. 단, 2 ≤ N ≤ 30,000, 2 ≤ d ≤ 3,000, 2 ≤

www.acmicpc.net

- 문제 -


- 풀이 과정 -

문제를 다시 풀어서 쉽게 말하자면 결국 연속된 숫자 중 중복되지 않은 숫자가 가장 많은 범위를 찾아 그 개수를 구하면 되는 문제입니다.

 

, 쿠폰으로 추가 초밥을 먹을 수 있다는 점을 유의해야 합니다. 연속된 숫자 범위에서 쿠폰으로 먹을 수 있는 초밥이 없으면 카운트를 +1해줘야 합니다.

 

연속된 숫자의 범위에서 답을 구하는 문제인 만큼 sliding window기법을 사용하여 풀어줄 수 있습니다.

window의 범위를 옮기면서 범위 내의 숫자의 개수를 체크해주면 되며 window의 범위는 문제에 제시된 k만큼 지정합니다.

 

window범위 내의 숫자의 개수를 세기위해 지정할 수 있는 범위의 숫자(1~30)만큼 배열을 하나 생성하여 체크해 줍니다.

…..

 

window 범위를 옮기다 보면 쿠폰이 포함되지 않은 범위가 나옵니다. 

문제에서 연속된 초밥을 먹고 쿠폰으로 먹을 수 있는 초밥이 포함되지 않았다면 하나 더 먹을 수 있다고 되어있는 부분이 나온 것입니다.

그렇기에 카운트를 +1을 해주어 쿠폰으로 먹을 수 있는 초밥을 포함시켜 줍니다.

…..

 

이런식으로 window의 범위를 옮겨가면서 window의 시작부분(start)이 마지막에 도달할 때까지 숫자의 개수를 체크합니다.

 

이때 window의 끝 부분(end)이 마지막에 도달한 경우 다시 처음부터 시작되도록 해야 합니다. (코드 상으로는 배열이지만 실제로는 벨트 형태로 구성되어야 하기 때문입니다.)

, window 끝 부분이 배열의 끝인 N의 범위를 넘어가게 되면 처음으로 돌아가도록 해야 하기에 코드에서 %N을 적용하였습니다.

 

window의 시작 부분이 배열의 마지막 부분까지 이동해 모든 범위의 체크가 완료되었다면 카운트가 가장 높았던 수를 출력해주면 됩니다.


- 소스 코드 -

package com.test;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    //2531번 - 회전 초밥
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int N,d,k,c;
    static int[] arr, eated;
    
    public static void main(String[] args) throws IOException{
        st=new StringTokenizer(br.readLine());
        N=Integer.parseInt(st.nextToken()); // 접시 갯수
        d=Integer.parseInt(st.nextToken()); // 초밥 갯수
        k=Integer.parseInt(st.nextToken()); // 연속 수
        c=Integer.parseInt(st.nextToken()); // 쿠폰 번호
        
        arr=new int[N]; // 접시 갯수 만큼 생성
        eated=new int[d+1]; // index 1부터 사용하기 위해 초밥 갯수+1 만큼 생성
        
        for(int i=0;i<N;i++) {
            arr[i]=Integer.parseInt(br.readLine());
        }
        System.out.println(rotate());
    }
    
    public static int rotate() {
        int count=0,max=0;
        
        // 처음 window범위의 숫자 개수 카운트
        for(int i=0;i<k;i++) {
            if(eated[arr[i]]==0) count++;           
            eated[arr[i]]++;
        }
        
        // 쿠폰 번호가 포함된 초밥이 없는 경우 count증가 
        if(eated[c]==0) count++;
        eated[c]++;         
        max=count;
        
        for(int i=1;i<N;i++) {          
            //window start 지점
            eated[arr[i-1]]--;
            if(eated[arr[i-1]]==0) count--;
            
            //window end 지점
            eated[arr[(i+k-1)%N]]++;
            if(eated[arr[(i+k-1)%N]]==1) count++;
            
            if(max<count) max=count;
        }
        return max;
    }
}

 

[참고 사이트]

사이트

https://pangtrue.tistory.com/278